Commit 0646a4e4 authored by Trond Myklebust's avatar Trond Myklebust

Merge http://nfsclient.bkbits.net/linux-2.6

into fys.uio.no:/home/linux/bitkeeper/nfsclient-2.6
parents 7d22476d b4a558fd
......@@ -1415,8 +1415,8 @@ config NFS_V3
bool "Provide NFSv3 client support"
depends on NFS_FS
help
Say Y here if you want your NFS client to be able to speak the newer
version 3 of the NFS protocol.
Say Y here if you want your NFS client to be able to speak version
3 of the NFS protocol.
If unsure, say Y.
......@@ -1560,6 +1560,22 @@ config RPCSEC_GSS_KRB5
If unsure, say N.
config RPCSEC_GSS_SPKM3
tristate "Secure RPC: SPKM3 mechanism (EXPERIMENTAL)"
depends on SUNRPC && EXPERIMENTAL
select SUNRPC_GSS
select CRYPTO
select CRYPTO_MD5
select CRYPTO_DES
help
Provides for secure RPC calls by means of a gss-api
mechanism based on the SPKM3 public-key mechanism.
Note: Requires an auxiliary userspace daemon which may be found on
http://www.citi.umich.edu/projects/nfsv4/
If unsure, say N.
config SMB_FS
tristate "SMB file system support (to mount Windows shares etc.)"
depends on INET
......
......@@ -237,8 +237,13 @@ nlmsvc_delete_block(struct nlm_block *block, int unlock)
/* Remove block from list */
nlmsvc_remove_block(block);
posix_unblock_lock(file->f_file, fl);
block->b_granted = 0;
if (fl->fl_next)
posix_unblock_lock(file->f_file, fl);
if (unlock) {
fl->fl_type = F_UNLCK;
posix_lock_file(file->f_file, fl);
block->b_granted = 0;
}
/* If the block is in the middle of a GRANT callback,
* don't kill it yet. */
......
......@@ -67,7 +67,7 @@ nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result,
down(&nlm_file_sema);
for (file = nlm_files[hash]; file; file = file->f_next)
if (!memcmp(&file->f_handle, f, sizeof(*f)))
if (!nfs_compare_fh(&file->f_handle, f))
goto found;
dprintk("lockd: creating file for (%08x %08x %08x %08x %08x %08x)\n",
......
......@@ -9,6 +9,7 @@ nfs-y := dir.o file.o inode.o nfs2xdr.o pagelist.o \
nfs-$(CONFIG_ROOT_NFS) += nfsroot.o mount_clnt.o
nfs-$(CONFIG_NFS_V3) += nfs3proc.o nfs3xdr.o
nfs-$(CONFIG_NFS_V4) += nfs4proc.o nfs4xdr.o nfs4state.o nfs4renewd.o \
idmap.o
delegation.o idmap.o \
callback.o callback_xdr.o callback_proc.o
nfs-$(CONFIG_NFS_DIRECTIO) += direct.o
nfs-objs := $(nfs-y)
/*
* linux/fs/nfs/callback.c
*
* Copyright (C) 2004 Trond Myklebust
*
* NFSv4 callback handling
*/
#include <linux/config.h>
#include <linux/completion.h>
#include <linux/ip.h>
#include <linux/module.h>
#include <linux/smp_lock.h>
#include <linux/sunrpc/svc.h>
#include <linux/sunrpc/svcsock.h>
#include <linux/nfs_fs.h>
#include "callback.h"
#define NFSDBG_FACILITY NFSDBG_CALLBACK
struct nfs_callback_data {
unsigned int users;
struct svc_serv *serv;
pid_t pid;
struct completion started;
struct completion stopped;
};
static struct nfs_callback_data nfs_callback_info;
static DECLARE_MUTEX(nfs_callback_sema);
static struct svc_program nfs4_callback_program;
unsigned short nfs_callback_tcpport;
/*
* This is the callback kernel thread.
*/
static void nfs_callback_svc(struct svc_rqst *rqstp)
{
struct svc_serv *serv = rqstp->rq_server;
int err;
__module_get(THIS_MODULE);
lock_kernel();
nfs_callback_info.pid = current->pid;
daemonize("nfsv4-svc");
/* Process request with signals blocked, but allow SIGKILL. */
allow_signal(SIGKILL);
complete(&nfs_callback_info.started);
while (nfs_callback_info.users != 0 || !signalled()) {
/*
* Listen for a request on the socket
*/
err = svc_recv(serv, rqstp, MAX_SCHEDULE_TIMEOUT);
if (err == -EAGAIN || err == -EINTR)
continue;
if (err < 0) {
printk(KERN_WARNING
"%s: terminating on error %d\n",
__FUNCTION__, -err);
break;
}
dprintk("%s: request from %u.%u.%u.%u\n", __FUNCTION__,
NIPQUAD(rqstp->rq_addr.sin_addr.s_addr));
svc_process(serv, rqstp);
}
nfs_callback_info.pid = 0;
complete(&nfs_callback_info.stopped);
unlock_kernel();
module_put_and_exit(0);
}
/*
* Bring up the server process if it is not already up.
*/
int nfs_callback_up(void)
{
struct svc_serv *serv;
struct svc_sock *svsk;
int ret = 0;
lock_kernel();
down(&nfs_callback_sema);
if (nfs_callback_info.users++ || nfs_callback_info.pid != 0)
goto out;
init_completion(&nfs_callback_info.started);
init_completion(&nfs_callback_info.stopped);
serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE);
ret = -ENOMEM;
if (!serv)
goto out_err;
/* FIXME: We don't want to register this socket with the portmapper */
ret = svc_makesock(serv, IPPROTO_TCP, 0);
if (ret < 0)
goto out_destroy;
if (!list_empty(&serv->sv_permsocks)) {
svsk = list_entry(serv->sv_permsocks.next,
struct svc_sock, sk_list);
nfs_callback_tcpport = ntohs(inet_sk(svsk->sk_sk)->sport);
dprintk ("Callback port = 0x%x\n", nfs_callback_tcpport);
} else
BUG();
ret = svc_create_thread(nfs_callback_svc, serv);
if (ret < 0)
goto out_destroy;
nfs_callback_info.serv = serv;
wait_for_completion(&nfs_callback_info.started);
out:
up(&nfs_callback_sema);
unlock_kernel();
return ret;
out_destroy:
svc_destroy(serv);
out_err:
nfs_callback_info.users--;
goto out;
}
/*
* Kill the server process if it is not already up.
*/
int nfs_callback_down(void)
{
int ret = 0;
lock_kernel();
down(&nfs_callback_sema);
if (--nfs_callback_info.users || nfs_callback_info.pid == 0)
goto out;
kill_proc(nfs_callback_info.pid, SIGKILL, 1);
wait_for_completion(&nfs_callback_info.stopped);
out:
up(&nfs_callback_sema);
unlock_kernel();
return ret;
}
/*
* AUTH_NULL authentication
*/
static int nfs_callback_null_accept(struct svc_rqst *rqstp, u32 *authp)
{
struct kvec *argv = &rqstp->rq_arg.head[0];
struct kvec *resv = &rqstp->rq_res.head[0];
if (argv->iov_len < 3*4)
return SVC_GARBAGE;
if (svc_getu32(argv) != 0) {
dprintk("svc: bad null cred\n");
*authp = rpc_autherr_badcred;
return SVC_DENIED;
}
if (svc_getu32(argv) != RPC_AUTH_NULL || svc_getu32(argv) != 0) {
dprintk("svc: bad null verf\n");
*authp = rpc_autherr_badverf;
return SVC_DENIED;
}
/* Signal that mapping to nobody uid/gid is required */
rqstp->rq_cred.cr_uid = (uid_t) -1;
rqstp->rq_cred.cr_gid = (gid_t) -1;
rqstp->rq_cred.cr_group_info = groups_alloc(0);
if (rqstp->rq_cred.cr_group_info == NULL)
return SVC_DROP; /* kmalloc failure - client must retry */
/* Put NULL verifier */
svc_putu32(resv, RPC_AUTH_NULL);
svc_putu32(resv, 0);
dprintk("%s: success, returning %d!\n", __FUNCTION__, SVC_OK);
return SVC_OK;
}
static int nfs_callback_null_release(struct svc_rqst *rqstp)
{
if (rqstp->rq_cred.cr_group_info)
put_group_info(rqstp->rq_cred.cr_group_info);
rqstp->rq_cred.cr_group_info = NULL;
return 0; /* don't drop */
}
static struct auth_ops nfs_callback_auth_null = {
.name = "null",
.flavour = RPC_AUTH_NULL,
.accept = nfs_callback_null_accept,
.release = nfs_callback_null_release,
};
/*
* AUTH_SYS authentication
*/
static int nfs_callback_unix_accept(struct svc_rqst *rqstp, u32 *authp)
{
struct kvec *argv = &rqstp->rq_arg.head[0];
struct kvec *resv = &rqstp->rq_res.head[0];
struct svc_cred *cred = &rqstp->rq_cred;
u32 slen, i;
int len = argv->iov_len;
dprintk("%s: start\n", __FUNCTION__);
cred->cr_group_info = NULL;
rqstp->rq_client = NULL;
if ((len -= 3*4) < 0)
return SVC_GARBAGE;
/* Get length, time stamp and machine name */
svc_getu32(argv);
svc_getu32(argv);
slen = XDR_QUADLEN(ntohl(svc_getu32(argv)));
if (slen > 64 || (len -= (slen + 3)*4) < 0)
goto badcred;
argv->iov_base = (void*)((u32*)argv->iov_base + slen);
argv->iov_len -= slen*4;
cred->cr_uid = ntohl(svc_getu32(argv));
cred->cr_gid = ntohl(svc_getu32(argv));
slen = ntohl(svc_getu32(argv));
if (slen > 16 || (len -= (slen + 2)*4) < 0)
goto badcred;
cred->cr_group_info = groups_alloc(slen);
if (cred->cr_group_info == NULL)
return SVC_DROP;
for (i = 0; i < slen; i++)
GROUP_AT(cred->cr_group_info, i) = ntohl(svc_getu32(argv));
if (svc_getu32(argv) != RPC_AUTH_NULL || svc_getu32(argv) != 0) {
*authp = rpc_autherr_badverf;
return SVC_DENIED;
}
/* Put NULL verifier */
svc_putu32(resv, RPC_AUTH_NULL);
svc_putu32(resv, 0);
dprintk("%s: success, returning %d!\n", __FUNCTION__, SVC_OK);
return SVC_OK;
badcred:
*authp = rpc_autherr_badcred;
return SVC_DENIED;
}
static int nfs_callback_unix_release(struct svc_rqst *rqstp)
{
if (rqstp->rq_cred.cr_group_info)
put_group_info(rqstp->rq_cred.cr_group_info);
rqstp->rq_cred.cr_group_info = NULL;
return 0;
}
static struct auth_ops nfs_callback_auth_unix = {
.name = "unix",
.flavour = RPC_AUTH_UNIX,
.accept = nfs_callback_unix_accept,
.release = nfs_callback_unix_release,
};
/*
* Hook the authentication protocol
*/
static int nfs_callback_auth(struct svc_rqst *rqstp, u32 *authp)
{
struct in_addr *addr = &rqstp->rq_addr.sin_addr;
struct nfs4_client *clp;
struct kvec *argv = &rqstp->rq_arg.head[0];
int flavour;
int retval;
/* Don't talk to strangers */
clp = nfs4_find_client(addr);
if (clp == NULL)
return SVC_DROP;
dprintk("%s: %u.%u.%u.%u NFSv4 callback!\n", __FUNCTION__, NIPQUAD(addr));
nfs4_put_client(clp);
flavour = ntohl(svc_getu32(argv));
switch(flavour) {
case RPC_AUTH_NULL:
if (rqstp->rq_proc != CB_NULL) {
*authp = rpc_autherr_tooweak;
retval = SVC_DENIED;
break;
}
rqstp->rq_authop = &nfs_callback_auth_null;
retval = nfs_callback_null_accept(rqstp, authp);
break;
case RPC_AUTH_UNIX:
/* Eat the authentication flavour */
rqstp->rq_authop = &nfs_callback_auth_unix;
retval = nfs_callback_unix_accept(rqstp, authp);
break;
default:
/* FIXME: need to add RPCSEC_GSS upcalls */
#if 0
svc_ungetu32(argv);
retval = svc_authenticate(rqstp, authp);
#else
*authp = rpc_autherr_rejectedcred;
retval = SVC_DENIED;
#endif
}
dprintk("%s: flavour %d returning error %d\n", __FUNCTION__, flavour, retval);
return retval;
}
/*
* Define NFS4 callback program
*/
extern struct svc_version nfs4_callback_version1;
static struct svc_version *nfs4_callback_version[] = {
[1] = &nfs4_callback_version1,
};
static struct svc_stat nfs4_callback_stats;
static struct svc_program nfs4_callback_program = {
.pg_prog = NFS4_CALLBACK, /* RPC service number */
.pg_nvers = ARRAY_SIZE(nfs4_callback_version), /* Number of entries */
.pg_vers = nfs4_callback_version, /* version table */
.pg_name = "NFSv4 callback", /* service name */
.pg_class = "nfs", /* authentication class */
.pg_stats = &nfs4_callback_stats,
.pg_authenticate = nfs_callback_auth,
};
/*
* linux/fs/nfs/callback.h
*
* Copyright (C) 2004 Trond Myklebust
*
* NFSv4 callback definitions
*/
#ifndef __LINUX_FS_NFS_CALLBACK_H
#define __LINUX_FS_NFS_CALLBACK_H
#define NFS4_CALLBACK 0x40000000
#define NFS4_CALLBACK_XDRSIZE 2048
#define NFS4_CALLBACK_BUFSIZE (1024 + NFS4_CALLBACK_XDRSIZE)
enum nfs4_callback_procnum {
CB_NULL = 0,
CB_COMPOUND = 1,
};
enum nfs4_callback_opnum {
OP_CB_GETATTR = 3,
OP_CB_RECALL = 4,
OP_CB_ILLEGAL = 10044,
};
struct cb_compound_hdr_arg {
int taglen;
const char *tag;
unsigned int callback_ident;
unsigned nops;
};
struct cb_compound_hdr_res {
uint32_t *status;
int taglen;
const char *tag;
uint32_t *nops;
};
struct cb_getattrargs {
struct sockaddr_in *addr;
struct nfs_fh fh;
uint32_t bitmap[2];
};
struct cb_getattrres {
uint32_t status;
uint32_t bitmap[2];
uint64_t size;
uint64_t change_attr;
struct timespec ctime;
struct timespec mtime;
};
struct cb_recallargs {
struct sockaddr_in *addr;
struct nfs_fh fh;
nfs4_stateid stateid;
uint32_t truncate;
};
extern unsigned nfs4_callback_getattr(struct cb_getattrargs *args, struct cb_getattrres *res);
extern unsigned nfs4_callback_recall(struct cb_recallargs *args, void *dummy);
extern int nfs_callback_up(void);
extern int nfs_callback_down(void);
extern unsigned short nfs_callback_tcpport;
#endif /* __LINUX_FS_NFS_CALLBACK_H */
/*
* linux/fs/nfs/callback_proc.c
*
* Copyright (C) 2004 Trond Myklebust
*
* NFSv4 callback procedures
*/
#include <linux/config.h>
#include <linux/nfs4.h>
#include <linux/nfs_fs.h>
#include "callback.h"
#include "delegation.h"
#define NFSDBG_FACILITY NFSDBG_CALLBACK
unsigned nfs4_callback_getattr(struct cb_getattrargs *args, struct cb_getattrres *res)
{
struct nfs4_client *clp;
struct nfs_delegation *delegation;
struct nfs_inode *nfsi;
struct inode *inode;
res->bitmap[0] = res->bitmap[1] = 0;
res->status = htonl(NFS4ERR_BADHANDLE);
clp = nfs4_find_client(&args->addr->sin_addr);
if (clp == NULL)
goto out;
inode = nfs_delegation_find_inode(clp, &args->fh);
if (inode == NULL)
goto out_putclient;
nfsi = NFS_I(inode);
down_read(&nfsi->rwsem);
delegation = nfsi->delegation;
if (delegation == NULL || (delegation->type & FMODE_WRITE) == 0)
goto out_iput;
res->size = i_size_read(inode);
res->change_attr = NFS_CHANGE_ATTR(inode);
res->ctime = inode->i_ctime;
res->mtime = inode->i_mtime;
res->bitmap[0] = (FATTR4_WORD0_CHANGE|FATTR4_WORD0_SIZE) &
args->bitmap[0];
res->bitmap[1] = (FATTR4_WORD1_TIME_METADATA|FATTR4_WORD1_TIME_MODIFY) &
args->bitmap[1];
res->status = 0;
out_iput:
up_read(&nfsi->rwsem);
iput(inode);
out_putclient:
nfs4_put_client(clp);
out:
dprintk("%s: exit with status = %d\n", __FUNCTION__, ntohl(res->status));
return res->status;
}
unsigned nfs4_callback_recall(struct cb_recallargs *args, void *dummy)
{
struct nfs4_client *clp;
struct inode *inode;
unsigned res;
res = htonl(NFS4ERR_BADHANDLE);
clp = nfs4_find_client(&args->addr->sin_addr);
if (clp == NULL)
goto out;
inode = nfs_delegation_find_inode(clp, &args->fh);
if (inode == NULL)
goto out_putclient;
/* Set up a helper thread to actually return the delegation */
switch(nfs_async_inode_return_delegation(inode, &args->stateid)) {
case 0:
res = 0;
break;
case -ENOENT:
res = htonl(NFS4ERR_BAD_STATEID);
break;
default:
res = htonl(NFS4ERR_RESOURCE);
}
iput(inode);
out_putclient:
nfs4_put_client(clp);
out:
dprintk("%s: exit with status = %d\n", __FUNCTION__, ntohl(res));
return res;
}
/*
* linux/fs/nfs/callback_xdr.c
*
* Copyright (C) 2004 Trond Myklebust
*
* NFSv4 callback encode/decode procedures
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sunrpc/svc.h>
#include <linux/nfs4.h>
#include <linux/nfs_fs.h>
#include "callback.h"
#define CB_OP_TAGLEN_MAXSZ (512)
#define CB_OP_HDR_RES_MAXSZ (2 + CB_OP_TAGLEN_MAXSZ)
#define CB_OP_GETATTR_BITMAP_MAXSZ (4)
#define CB_OP_GETATTR_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ + \
CB_OP_GETATTR_BITMAP_MAXSZ + \
2 + 2 + 3 + 3)
#define CB_OP_RECALL_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ)
#define NFSDBG_FACILITY NFSDBG_CALLBACK
typedef unsigned (*callback_process_op_t)(void *, void *);
typedef unsigned (*callback_decode_arg_t)(struct svc_rqst *, struct xdr_stream *, void *);
typedef unsigned (*callback_encode_res_t)(struct svc_rqst *, struct xdr_stream *, void *);
struct callback_op {
callback_process_op_t process_op;
callback_decode_arg_t decode_args;
callback_encode_res_t encode_res;
long res_maxsize;
};
static struct callback_op callback_ops[];
static int nfs4_callback_null(struct svc_rqst *rqstp, void *argp, void *resp)
{
return htonl(NFS4_OK);
}
static int nfs4_decode_void(struct svc_rqst *rqstp, uint32_t *p, void *dummy)
{
return xdr_argsize_check(rqstp, p);
}
static int nfs4_encode_void(struct svc_rqst *rqstp, uint32_t *p, void *dummy)
{
return xdr_ressize_check(rqstp, p);
}
static uint32_t *read_buf(struct xdr_stream *xdr, int nbytes)
{
uint32_t *p;
p = xdr_inline_decode(xdr, nbytes);
if (unlikely(p == NULL))
printk(KERN_WARNING "NFSv4 callback reply buffer overflowed!\n");
return p;
}
static unsigned decode_string(struct xdr_stream *xdr, unsigned int *len, const char **str)
{
uint32_t *p;
p = read_buf(xdr, 4);
if (unlikely(p == NULL))
return htonl(NFS4ERR_RESOURCE);
*len = ntohl(*p);
if (*len != 0) {
p = read_buf(xdr, *len);
if (unlikely(p == NULL))
return htonl(NFS4ERR_RESOURCE);
*str = (const char *)p;
} else
*str = NULL;
return 0;
}
static unsigned decode_fh(struct xdr_stream *xdr, struct nfs_fh *fh)
{
uint32_t *p;
p = read_buf(xdr, 4);
if (unlikely(p == NULL))
return htonl(NFS4ERR_RESOURCE);
fh->size = ntohl(*p);
if (fh->size > NFS4_FHSIZE)
return htonl(NFS4ERR_BADHANDLE);
p = read_buf(xdr, fh->size);
if (unlikely(p == NULL))
return htonl(NFS4ERR_RESOURCE);
memcpy(&fh->data[0], p, fh->size);
memset(&fh->data[fh->size], 0, sizeof(fh->data) - fh->size);
return 0;
}
static unsigned decode_bitmap(struct xdr_stream *xdr, uint32_t *bitmap)
{
uint32_t *p;
unsigned int attrlen;
p = read_buf(xdr, 4);
if (unlikely(p == NULL))
return htonl(NFS4ERR_RESOURCE);
attrlen = ntohl(*p);
p = read_buf(xdr, attrlen << 2);
if (unlikely(p == NULL))
return htonl(NFS4ERR_RESOURCE);
if (likely(attrlen > 0))
bitmap[0] = ntohl(*p++);
if (attrlen > 1)
bitmap[1] = ntohl(*p);
return 0;
}
static unsigned decode_stateid(struct xdr_stream *xdr, nfs4_stateid *stateid)
{
uint32_t *p;
p = read_buf(xdr, 16);
if (unlikely(p == NULL))
return htonl(NFS4ERR_RESOURCE);
memcpy(stateid->data, p, 16);
return 0;
}
static unsigned decode_compound_hdr_arg(struct xdr_stream *xdr, struct cb_compound_hdr_arg *hdr)
{
uint32_t *p;
unsigned int minor_version;
unsigned status;
status = decode_string(xdr, &hdr->taglen, &hdr->tag);
if (unlikely(status != 0))
return status;
/* We do not like overly long tags! */
if (hdr->taglen > CB_OP_TAGLEN_MAXSZ-12 || hdr->taglen < 0) {
printk("NFSv4 CALLBACK %s: client sent tag of length %u\n",
__FUNCTION__, hdr->taglen);
return htonl(NFS4ERR_RESOURCE);
}
p = read_buf(xdr, 12);
if (unlikely(p == NULL))
return htonl(NFS4ERR_RESOURCE);
minor_version = ntohl(*p++);
/* Check minor version is zero. */
if (minor_version != 0) {
printk(KERN_WARNING "%s: NFSv4 server callback with illegal minor version %u!\n",
__FUNCTION__, minor_version);
return htonl(NFS4ERR_MINOR_VERS_MISMATCH);
}
hdr->callback_ident = ntohl(*p++);
hdr->nops = ntohl(*p);
return 0;
}
static unsigned decode_op_hdr(struct xdr_stream *xdr, unsigned int *op)
{
uint32_t *p;
p = read_buf(xdr, 4);
if (unlikely(p == NULL))
return htonl(NFS4ERR_RESOURCE);
*op = ntohl(*p);
return 0;
}
static unsigned decode_getattr_args(struct svc_rqst *rqstp, struct xdr_stream *xdr, struct cb_getattrargs *args)
{
unsigned status;
status = decode_fh(xdr, &args->fh);
if (unlikely(status != 0))
goto out;
args->addr = &rqstp->rq_addr;
status = decode_bitmap(xdr, args->bitmap);
out:
dprintk("%s: exit with status = %d\n", __FUNCTION__, status);
return status;
}
static unsigned decode_recall_args(struct svc_rqst *rqstp, struct xdr_stream *xdr, struct cb_recallargs *args)
{
uint32_t *p;
unsigned status;
args->addr = &rqstp->rq_addr;
status = decode_stateid(xdr, &args->stateid);
if (unlikely(status != 0))
goto out;
p = read_buf(xdr, 4);
if (unlikely(p == NULL)) {
status = htonl(NFS4ERR_RESOURCE);
goto out;
}
args->truncate = ntohl(*p);
status = decode_fh(xdr, &args->fh);
out:
dprintk("%s: exit with status = %d\n", __FUNCTION__, status);
return 0;
}
static unsigned encode_string(struct xdr_stream *xdr, unsigned int len, const char *str)
{
uint32_t *p;
p = xdr_reserve_space(xdr, 4 + len);
if (unlikely(p == NULL))
return htonl(NFS4ERR_RESOURCE);
xdr_encode_opaque(p, str, len);
return 0;
}
#define CB_SUPPORTED_ATTR0 (FATTR4_WORD0_CHANGE|FATTR4_WORD0_SIZE)
#define CB_SUPPORTED_ATTR1 (FATTR4_WORD1_TIME_METADATA|FATTR4_WORD1_TIME_MODIFY)
static unsigned encode_attr_bitmap(struct xdr_stream *xdr, const uint32_t *bitmap, uint32_t **savep)
{
uint32_t bm[2];
uint32_t *p;
bm[0] = htonl(bitmap[0] & CB_SUPPORTED_ATTR0);
bm[1] = htonl(bitmap[1] & CB_SUPPORTED_ATTR1);
if (bm[1] != 0) {
p = xdr_reserve_space(xdr, 16);
if (unlikely(p == NULL))
return htonl(NFS4ERR_RESOURCE);
*p++ = htonl(2);
*p++ = bm[0];
*p++ = bm[1];
} else if (bm[0] != 0) {
p = xdr_reserve_space(xdr, 12);
if (unlikely(p == NULL))
return htonl(NFS4ERR_RESOURCE);
*p++ = htonl(1);
*p++ = bm[0];
} else {
p = xdr_reserve_space(xdr, 8);
if (unlikely(p == NULL))
return htonl(NFS4ERR_RESOURCE);
*p++ = htonl(0);
}
*savep = p;
return 0;
}
static unsigned encode_attr_change(struct xdr_stream *xdr, const uint32_t *bitmap, uint64_t change)
{
uint32_t *p;
if (!(bitmap[0] & FATTR4_WORD0_CHANGE))
return 0;
p = xdr_reserve_space(xdr, 8);
if (unlikely(p == 0))
return htonl(NFS4ERR_RESOURCE);
p = xdr_encode_hyper(p, change);
return 0;
}
static unsigned encode_attr_size(struct xdr_stream *xdr, const uint32_t *bitmap, uint64_t size)
{
uint32_t *p;
if (!(bitmap[0] & FATTR4_WORD0_SIZE))
return 0;
p = xdr_reserve_space(xdr, 8);
if (unlikely(p == 0))
return htonl(NFS4ERR_RESOURCE);
p = xdr_encode_hyper(p, size);
return 0;
}
static unsigned encode_attr_time(struct xdr_stream *xdr, const struct timespec *time)
{
uint32_t *p;
p = xdr_reserve_space(xdr, 12);
if (unlikely(p == 0))
return htonl(NFS4ERR_RESOURCE);
p = xdr_encode_hyper(p, time->tv_sec);
*p = htonl(time->tv_nsec);
return 0;
}
static unsigned encode_attr_ctime(struct xdr_stream *xdr, const uint32_t *bitmap, const struct timespec *time)
{
if (!(bitmap[1] & FATTR4_WORD1_TIME_METADATA))
return 0;
return encode_attr_time(xdr,time);
}
static unsigned encode_attr_mtime(struct xdr_stream *xdr, const uint32_t *bitmap, const struct timespec *time)
{
if (!(bitmap[1] & FATTR4_WORD1_TIME_MODIFY))
return 0;
return encode_attr_time(xdr,time);
}
static unsigned encode_compound_hdr_res(struct xdr_stream *xdr, struct cb_compound_hdr_res *hdr)
{
unsigned status;
hdr->status = xdr_reserve_space(xdr, 4);
if (unlikely(hdr->status == NULL))
return htonl(NFS4ERR_RESOURCE);
status = encode_string(xdr, hdr->taglen, hdr->tag);
if (unlikely(status != 0))
return status;
hdr->nops = xdr_reserve_space(xdr, 4);
if (unlikely(hdr->nops == NULL))
return htonl(NFS4ERR_RESOURCE);
return 0;
}
static unsigned encode_op_hdr(struct xdr_stream *xdr, uint32_t op, uint32_t res)
{
uint32_t *p;
p = xdr_reserve_space(xdr, 8);
if (unlikely(p == NULL))
return htonl(NFS4ERR_RESOURCE);
*p++ = htonl(op);
*p = res;
return 0;
}
static unsigned encode_getattr_res(struct svc_rqst *rqstp, struct xdr_stream *xdr, const struct cb_getattrres *res)
{
uint32_t *savep;
unsigned status = res->status;
if (unlikely(status != 0))
goto out;
status = encode_attr_bitmap(xdr, res->bitmap, &savep);
if (unlikely(status != 0))
goto out;
status = encode_attr_change(xdr, res->bitmap, res->change_attr);
if (unlikely(status != 0))
goto out;
status = encode_attr_size(xdr, res->bitmap, res->size);
if (unlikely(status != 0))
goto out;
status = encode_attr_ctime(xdr, res->bitmap, &res->ctime);
if (unlikely(status != 0))
goto out;
status = encode_attr_mtime(xdr, res->bitmap, &res->mtime);
*savep = htonl((unsigned int)((char *)xdr->p - (char *)(savep+1)));
out:
dprintk("%s: exit with status = %d\n", __FUNCTION__, status);
return status;
}
static unsigned process_op(struct svc_rqst *rqstp,
struct xdr_stream *xdr_in, void *argp,
struct xdr_stream *xdr_out, void *resp)
{
struct callback_op *op;
unsigned int op_nr;
unsigned int status = 0;
long maxlen;
unsigned res;
dprintk("%s: start\n", __FUNCTION__);
status = decode_op_hdr(xdr_in, &op_nr);
if (unlikely(status != 0)) {
op_nr = OP_CB_ILLEGAL;
op = &callback_ops[0];
} else if (unlikely(op_nr != OP_CB_GETATTR && op_nr != OP_CB_RECALL)) {
op_nr = OP_CB_ILLEGAL;
op = &callback_ops[0];
status = htonl(NFS4ERR_OP_ILLEGAL);
} else
op = &callback_ops[op_nr];
maxlen = xdr_out->end - xdr_out->p;
if (maxlen > 0 && maxlen < PAGE_SIZE) {
if (likely(status == 0 && op->decode_args != NULL))
status = op->decode_args(rqstp, xdr_in, argp);
if (likely(status == 0 && op->process_op != NULL))
status = op->process_op(argp, resp);
} else
status = htonl(NFS4ERR_RESOURCE);
res = encode_op_hdr(xdr_out, op_nr, status);
if (status == 0)
status = res;
if (op->encode_res != NULL && status == 0)
status = op->encode_res(rqstp, xdr_out, resp);
dprintk("%s: done, status = %d\n", __FUNCTION__, status);
return status;
}
/*
* Decode, process and encode a COMPOUND
*/
static int nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *resp)
{
struct cb_compound_hdr_arg hdr_arg;
struct cb_compound_hdr_res hdr_res;
struct xdr_stream xdr_in, xdr_out;
uint32_t *p;
unsigned int status;
unsigned int nops = 1;
dprintk("%s: start\n", __FUNCTION__);
xdr_init_decode(&xdr_in, &rqstp->rq_arg, rqstp->rq_arg.head[0].iov_base);
p = (uint32_t*)((char *)rqstp->rq_res.head[0].iov_base + rqstp->rq_res.head[0].iov_len);
rqstp->rq_res.head[0].iov_len = PAGE_SIZE;
xdr_init_encode(&xdr_out, &rqstp->rq_res, p);
decode_compound_hdr_arg(&xdr_in, &hdr_arg);
hdr_res.taglen = hdr_arg.taglen;
hdr_res.tag = hdr_arg.tag;
encode_compound_hdr_res(&xdr_out, &hdr_res);
for (;;) {
status = process_op(rqstp, &xdr_in, argp, &xdr_out, resp);
if (status != 0)
break;
if (nops == hdr_arg.nops)
break;
nops++;
}
*hdr_res.status = status;
*hdr_res.nops = htonl(nops);
dprintk("%s: done, status = %u\n", __FUNCTION__, status);
return rpc_success;
}
/*
* Define NFS4 callback COMPOUND ops.
*/
static struct callback_op callback_ops[] = {
[0] = {
.res_maxsize = CB_OP_HDR_RES_MAXSZ,
},
[OP_CB_GETATTR] = {
.process_op = (callback_process_op_t)nfs4_callback_getattr,
.decode_args = (callback_decode_arg_t)decode_getattr_args,
.encode_res = (callback_encode_res_t)encode_getattr_res,
.res_maxsize = CB_OP_GETATTR_RES_MAXSZ,
},
[OP_CB_RECALL] = {
.process_op = (callback_process_op_t)nfs4_callback_recall,
.decode_args = (callback_decode_arg_t)decode_recall_args,
.res_maxsize = CB_OP_RECALL_RES_MAXSZ,
}
};
/*
* Define NFS4 callback procedures
*/
static struct svc_procedure nfs4_callback_procedures1[] = {
[CB_NULL] = {
.pc_func = nfs4_callback_null,
.pc_decode = (kxdrproc_t)nfs4_decode_void,
.pc_encode = (kxdrproc_t)nfs4_encode_void,
.pc_xdrressize = 1,
},
[CB_COMPOUND] = {
.pc_func = nfs4_callback_compound,
.pc_encode = (kxdrproc_t)nfs4_encode_void,
.pc_argsize = 256,
.pc_ressize = 256,
.pc_xdrressize = NFS4_CALLBACK_BUFSIZE,
}
};
struct svc_version nfs4_callback_version1 = {
.vs_vers = 1,
.vs_nproc = ARRAY_SIZE(nfs4_callback_procedures1),
.vs_proc = nfs4_callback_procedures1,
.vs_xdrsize = NFS4_CALLBACK_XDRSIZE,
.vs_dispatch = NULL,
};
/*
* linux/fs/nfs/delegation.c
*
* Copyright (C) 2004 Trond Myklebust
*
* NFS file delegation management
*
*/
#include <linux/config.h>
#include <linux/completion.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/nfs4.h>
#include <linux/nfs_fs.h>
#include <linux/nfs_xdr.h>
#include "delegation.h"
static struct nfs_delegation *nfs_alloc_delegation(void)
{
return (struct nfs_delegation *)kmalloc(sizeof(struct nfs_delegation), GFP_KERNEL);
}
static void nfs_free_delegation(struct nfs_delegation *delegation)
{
if (delegation->cred)
put_rpccred(delegation->cred);
kfree(delegation);
}
static void nfs_delegation_claim_opens(struct inode *inode)
{
struct nfs_inode *nfsi = NFS_I(inode);
struct nfs_open_context *ctx;
struct nfs4_state *state;
again:
spin_lock(&inode->i_lock);
list_for_each_entry(ctx, &nfsi->open_files, list) {
state = ctx->state;
if (state == NULL)
continue;
if (!test_bit(NFS_DELEGATED_STATE, &state->flags))
continue;
get_nfs_open_context(ctx);
spin_unlock(&inode->i_lock);
if (nfs4_open_delegation_recall(ctx->dentry, state) < 0)
return;
put_nfs_open_context(ctx);
goto again;
}
spin_unlock(&inode->i_lock);
}
/*
* Set up a delegation on an inode
*/
void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res)
{
struct nfs_delegation *delegation = NFS_I(inode)->delegation;
if (delegation == NULL)
return;
memcpy(delegation->stateid.data, res->delegation.data,
sizeof(delegation->stateid.data));
delegation->type = res->delegation_type;
delegation->maxsize = res->maxsize;
put_rpccred(cred);
delegation->cred = get_rpccred(cred);
delegation->flags &= ~NFS_DELEGATION_NEED_RECLAIM;
NFS_I(inode)->delegation_state = delegation->type;
smp_wmb();
}
/*
* Set up a delegation on an inode
*/
int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res)
{
struct nfs4_client *clp = NFS_SERVER(inode)->nfs4_state;
struct nfs_inode *nfsi = NFS_I(inode);
struct nfs_delegation *delegation;
int status = 0;
delegation = nfs_alloc_delegation();
if (delegation == NULL)
return -ENOMEM;
memcpy(delegation->stateid.data, res->delegation.data,
sizeof(delegation->stateid.data));
delegation->type = res->delegation_type;
delegation->maxsize = res->maxsize;
delegation->cred = get_rpccred(cred);
delegation->inode = inode;
spin_lock(&clp->cl_lock);
if (nfsi->delegation == NULL) {
list_add(&delegation->super_list, &clp->cl_delegations);
nfsi->delegation = delegation;
nfsi->delegation_state = delegation->type;
delegation = NULL;
} else {
if (memcmp(&delegation->stateid, &nfsi->delegation->stateid,
sizeof(delegation->stateid)) != 0 ||
delegation->type != nfsi->delegation->type) {
printk("%s: server %u.%u.%u.%u, handed out a duplicate delegation!\n",
__FUNCTION__, NIPQUAD(clp->cl_addr));
status = -EIO;
}
}
spin_unlock(&clp->cl_lock);
if (delegation != NULL)
kfree(delegation);
return status;
}
static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *delegation)
{
int res = 0;
__nfs_revalidate_inode(NFS_SERVER(inode), inode);
res = nfs4_proc_delegreturn(inode, delegation->cred, &delegation->stateid);
nfs_free_delegation(delegation);
return res;
}
/* Sync all data to disk upon delegation return */
static void nfs_msync_inode(struct inode *inode)
{
filemap_fdatawrite(inode->i_mapping);
nfs_wb_all(inode);
filemap_fdatawait(inode->i_mapping);
}
/*
* Basic procedure for returning a delegation to the server
*/
int nfs_inode_return_delegation(struct inode *inode)
{
struct nfs4_client *clp = NFS_SERVER(inode)->nfs4_state;
struct nfs_inode *nfsi = NFS_I(inode);
struct nfs_delegation *delegation;
int res = 0;
nfs_msync_inode(inode);
down_read(&clp->cl_sem);
/* Guard against new delegated open calls */
down_write(&nfsi->rwsem);
spin_lock(&clp->cl_lock);
delegation = nfsi->delegation;
if (delegation != NULL) {
list_del_init(&delegation->super_list);
nfsi->delegation = NULL;
nfsi->delegation_state = 0;
}
spin_unlock(&clp->cl_lock);
nfs_delegation_claim_opens(inode);
up_write(&nfsi->rwsem);
up_read(&clp->cl_sem);
nfs_msync_inode(inode);
if (delegation != NULL)
res = nfs_do_return_delegation(inode, delegation);
return res;
}
/*
* Return all delegations associated to a super block
*/
void nfs_return_all_delegations(struct super_block *sb)
{
struct nfs4_client *clp = NFS_SB(sb)->nfs4_state;
struct nfs_delegation *delegation;
struct inode *inode;
if (clp == NULL)
return;
restart:
spin_lock(&clp->cl_lock);
list_for_each_entry(delegation, &clp->cl_delegations, super_list) {
if (delegation->inode->i_sb != sb)
continue;
inode = igrab(delegation->inode);
if (inode == NULL)
continue;
spin_unlock(&clp->cl_lock);
nfs_inode_return_delegation(inode);
iput(inode);
goto restart;
}
spin_unlock(&clp->cl_lock);
}
/*
* Return all delegations following an NFS4ERR_CB_PATH_DOWN error.
*/
void nfs_handle_cb_pathdown(struct nfs4_client *clp)
{
struct nfs_delegation *delegation;
struct inode *inode;
if (clp == NULL)
return;
restart:
spin_lock(&clp->cl_lock);
list_for_each_entry(delegation, &clp->cl_delegations, super_list) {
inode = igrab(delegation->inode);
if (inode == NULL)
continue;
spin_unlock(&clp->cl_lock);
nfs_inode_return_delegation(inode);
iput(inode);
goto restart;
}
spin_unlock(&clp->cl_lock);
}
struct recall_threadargs {
struct inode *inode;
struct nfs4_client *clp;
const nfs4_stateid *stateid;
struct completion started;
int result;
};
static int recall_thread(void *data)
{
struct recall_threadargs *args = (struct recall_threadargs *)data;
struct inode *inode = igrab(args->inode);
struct nfs4_client *clp = NFS_SERVER(inode)->nfs4_state;
struct nfs_inode *nfsi = NFS_I(inode);
struct nfs_delegation *delegation;
daemonize("nfsv4-delegreturn");
nfs_msync_inode(inode);
down_read(&clp->cl_sem);
down_write(&nfsi->rwsem);
spin_lock(&clp->cl_lock);
delegation = nfsi->delegation;
if (delegation != NULL && memcmp(delegation->stateid.data,
args->stateid->data,
sizeof(delegation->stateid.data)) == 0) {
list_del_init(&delegation->super_list);
nfsi->delegation = NULL;
nfsi->delegation_state = 0;
args->result = 0;
} else {
delegation = NULL;
args->result = -ENOENT;
}
spin_unlock(&clp->cl_lock);
complete(&args->started);
nfs_delegation_claim_opens(inode);
up_write(&nfsi->rwsem);
up_read(&clp->cl_sem);
nfs_msync_inode(inode);
if (delegation != NULL)
nfs_do_return_delegation(inode, delegation);
iput(inode);
module_put_and_exit(0);
}
/*
* Asynchronous delegation recall!
*/
int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid)
{
struct recall_threadargs data = {
.inode = inode,
.stateid = stateid,
};
int status;
init_completion(&data.started);
__module_get(THIS_MODULE);
status = kernel_thread(recall_thread, &data, CLONE_KERNEL);
if (status < 0)
goto out_module_put;
wait_for_completion(&data.started);
return data.result;
out_module_put:
module_put(THIS_MODULE);
return status;
}
/*
* Retrieve the inode associated with a delegation
*/
struct inode *nfs_delegation_find_inode(struct nfs4_client *clp, const struct nfs_fh *fhandle)
{
struct nfs_delegation *delegation;
struct inode *res = NULL;
spin_lock(&clp->cl_lock);
list_for_each_entry(delegation, &clp->cl_delegations, super_list) {
if (nfs_compare_fh(fhandle, &NFS_I(delegation->inode)->fh) == 0) {
res = igrab(delegation->inode);
break;
}
}
spin_unlock(&clp->cl_lock);
return res;
}
/*
* Mark all delegations as needing to be reclaimed
*/
void nfs_delegation_mark_reclaim(struct nfs4_client *clp)
{
struct nfs_delegation *delegation;
spin_lock(&clp->cl_lock);
list_for_each_entry(delegation, &clp->cl_delegations, super_list)
delegation->flags |= NFS_DELEGATION_NEED_RECLAIM;
spin_unlock(&clp->cl_lock);
}
/*
* Reap all unclaimed delegations after reboot recovery is done
*/
void nfs_delegation_reap_unclaimed(struct nfs4_client *clp)
{
struct nfs_delegation *delegation, *n;
LIST_HEAD(head);
spin_lock(&clp->cl_lock);
list_for_each_entry_safe(delegation, n, &clp->cl_delegations, super_list) {
if ((delegation->flags & NFS_DELEGATION_NEED_RECLAIM) == 0)
continue;
list_move(&delegation->super_list, &head);
NFS_I(delegation->inode)->delegation = NULL;
NFS_I(delegation->inode)->delegation_state = 0;
}
spin_unlock(&clp->cl_lock);
while(!list_empty(&head)) {
delegation = list_entry(head.next, struct nfs_delegation, super_list);
list_del(&delegation->super_list);
nfs_free_delegation(delegation);
}
}
/*
* linux/fs/nfs/delegation.h
*
* Copyright (c) Trond Myklebust
*
* Definitions pertaining to NFS delegated files
*/
#ifndef FS_NFS_DELEGATION_H
#define FS_NFS_DELEGATION_H
#if defined(CONFIG_NFS_V4)
/*
* NFSv4 delegation
*/
struct nfs_delegation {
struct list_head super_list;
struct rpc_cred *cred;
struct inode *inode;
nfs4_stateid stateid;
int type;
#define NFS_DELEGATION_NEED_RECLAIM 1
long flags;
loff_t maxsize;
};
int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res);
void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res);
int nfs_inode_return_delegation(struct inode *inode);
int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid);
struct inode *nfs_delegation_find_inode(struct nfs4_client *clp, const struct nfs_fh *fhandle);
void nfs_return_all_delegations(struct super_block *sb);
void nfs_handle_cb_pathdown(struct nfs4_client *clp);
void nfs_delegation_mark_reclaim(struct nfs4_client *clp);
void nfs_delegation_reap_unclaimed(struct nfs4_client *clp);
/* NFSv4 delegation-related procedures */
int nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, const nfs4_stateid *stateid);
int nfs4_open_delegation_recall(struct dentry *dentry, struct nfs4_state *state);
static inline int nfs_have_delegation(struct inode *inode, int flags)
{
flags &= FMODE_READ|FMODE_WRITE;
smp_rmb();
if ((NFS_I(inode)->delegation_state & flags) == flags)
return 1;
return 0;
}
#else
static inline int nfs_have_delegation(struct inode *inode, int flags)
{
return 0;
}
#endif
#endif
......@@ -32,6 +32,8 @@
#include <linux/smp_lock.h>
#include <linux/namei.h>
#include "delegation.h"
#define NFS_PARANOIA 1
/* #define NFS_DEBUG_VERBOSE 1 */
......@@ -610,7 +612,7 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd)
verifier = nfs_save_change_attribute(dir);
error = nfs_cached_lookup(dir, dentry, &fhandle, &fattr);
if (!error) {
if (memcmp(NFS_FH(inode), &fhandle, sizeof(struct nfs_fh))!= 0)
if (nfs_compare_fh(NFS_FH(inode), &fhandle))
goto out_bad;
if (nfs_lookup_verify_inode(inode, isopen))
goto out_zap_parent;
......@@ -623,7 +625,7 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd)
error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, &fhandle, &fattr);
if (error)
goto out_bad;
if (memcmp(NFS_FH(inode), &fhandle, sizeof(struct nfs_fh))!= 0)
if (nfs_compare_fh(NFS_FH(inode), &fhandle))
goto out_bad;
if ((error = nfs_refresh_inode(inode, &fattr)) != 0)
goto out_bad;
......@@ -850,22 +852,22 @@ static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd)
unsigned long verifier;
int openflags, ret = 0;
/* NFS only supports OPEN for regular files */
if (inode && !S_ISREG(inode->i_mode))
goto no_open;
parent = dget_parent(dentry);
dir = parent->d_inode;
if (!is_atomic_open(dir, nd))
goto no_open;
/* We can't create new files in nfs_open_revalidate(), so we
* optimize away revalidation of negative dentries.
*/
if (inode == NULL)
goto out;
/* NFS only supports OPEN on regular files */
if (!S_ISREG(inode->i_mode))
goto no_open;
openflags = nd->intent.open.flags;
if (openflags & O_CREAT) {
/* If this is a negative dentry, just drop it */
if (!inode)
goto out;
/* If this is exclusive open, just revalidate */
if (openflags & O_EXCL)
goto no_open;
}
/* We cannot do exclusive creation on a positive dentry */
if ((openflags & (O_CREAT|O_EXCL)) == (O_CREAT|O_EXCL))
goto no_open;
/* We can't create new files, or truncate existing ones here */
openflags &= ~(O_CREAT|O_TRUNC);
......@@ -887,6 +889,8 @@ static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd)
return ret;
no_open:
dput(parent);
if (inode != NULL && nfs_have_delegation(inode, FMODE_READ))
return 1;
return nfs_lookup_revalidate(dentry, nd);
}
#endif /* CONFIG_NFSV4 */
......@@ -982,12 +986,18 @@ static int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fhandle,
/* We may have been initialized further down */
if (dentry->d_inode)
return 0;
if (fhandle->size == 0 || !(fattr->valid & NFS_ATTR_FATTR)) {
if (fhandle->size == 0) {
struct inode *dir = dentry->d_parent->d_inode;
error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr);
if (error)
goto out_err;
}
if (!(fattr->valid & NFS_ATTR_FATTR)) {
struct nfs_server *server = NFS_SB(dentry->d_sb);
error = server->rpc_ops->getattr(server, fhandle, fattr);
if (error < 0)
goto out_err;
}
inode = nfs_fhget(dentry->d_sb, fhandle, fattr);
if (inode) {
d_instantiate(dentry, inode);
......@@ -1299,19 +1309,6 @@ nfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
dfprintk(VFS, "NFS: symlink(%s/%ld, %s, %s)\n", dir->i_sb->s_id,
dir->i_ino, dentry->d_name.name, symname);
error = -ENAMETOOLONG;
switch (NFS_PROTO(dir)->version) {
case 2:
if (strlen(symname) > NFS2_MAXPATHLEN)
goto out;
break;
case 3:
if (strlen(symname) > NFS3_MAXPATHLEN)
goto out;
default:
break;
}
#ifdef NFS_PARANOIA
if (dentry->d_inode)
printk("nfs_proc_symlink: %s/%s not negative!\n",
......@@ -1341,8 +1338,6 @@ dentry->d_parent->d_name.name, dentry->d_name.name);
d_drop(dentry);
}
unlock_kernel();
out:
return error;
}
......@@ -1498,10 +1493,56 @@ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
return error;
}
int
nfs_permission(struct inode *inode, int mask, struct nameidata *nd)
int nfs_access_get_cached(struct inode *inode, struct rpc_cred *cred, struct nfs_access_entry *res)
{
struct nfs_access_entry *cache = &NFS_I(inode)->cache_access;
if (cache->cred != cred
|| time_after(jiffies, cache->jiffies + NFS_ATTRTIMEO(inode))
|| (NFS_FLAGS(inode) & NFS_INO_INVALID_ATTR))
return -ENOENT;
memcpy(res, cache, sizeof(*res));
return 0;
}
void nfs_access_add_cache(struct inode *inode, struct nfs_access_entry *set)
{
struct nfs_access_entry *cache = &NFS_I(inode)->cache_access;
if (cache->cred != set->cred) {
if (cache->cred)
put_rpccred(cache->cred);
cache->cred = get_rpccred(set->cred);
}
cache->jiffies = set->jiffies;
cache->mask = set->mask;
}
static int nfs_do_access(struct inode *inode, struct rpc_cred *cred, int mask)
{
struct nfs_access_entry cache;
int status;
status = nfs_access_get_cached(inode, cred, &cache);
if (status == 0)
goto out;
/* Be clever: ask server to check for all possible rights */
cache.mask = MAY_EXEC | MAY_WRITE | MAY_READ;
cache.cred = cred;
cache.jiffies = jiffies;
status = NFS_PROTO(inode)->access(inode, &cache);
if (status != 0)
return status;
nfs_access_add_cache(inode, &cache);
out:
if ((cache.mask & mask) == mask)
return 0;
return -EACCES;
}
int nfs_permission(struct inode *inode, int mask, struct nameidata *nd)
{
struct nfs_access_cache *cache = &NFS_I(inode)->cache_access;
struct rpc_cred *cred;
int mode = inode->i_mode;
int res;
......@@ -1542,24 +1583,7 @@ nfs_permission(struct inode *inode, int mask, struct nameidata *nd)
goto out_notsup;
cred = rpcauth_lookupcred(NFS_CLIENT(inode)->cl_auth, 0);
if (cache->cred == cred
&& time_before(jiffies, cache->jiffies + NFS_ATTRTIMEO(inode))
&& !(NFS_FLAGS(inode) & NFS_INO_INVALID_ATTR)) {
if (!(res = cache->err)) {
/* Is the mask a subset of an accepted mask? */
if ((cache->mask & mask) == mask)
goto out;
} else {
/* ...or is it a superset of a rejected mask? */
if ((cache->mask & mask) == cache->mask)
goto out;
}
}
res = NFS_PROTO(inode)->access(inode, cred, mask);
if (!res || res == -EACCES)
goto add_cache;
out:
res = nfs_do_access(inode, cred, mask);
put_rpccred(cred);
unlock_kernel();
return res;
......@@ -1568,15 +1592,6 @@ nfs_permission(struct inode *inode, int mask, struct nameidata *nd)
res = vfs_permission(inode, mask);
unlock_kernel();
return res;
add_cache:
cache->jiffies = jiffies;
if (cache->cred)
put_rpccred(cache->cred);
cache->cred = cred;
cache->mask = mask;
cache->err = res;
unlock_kernel();
return res;
}
/*
......
......@@ -110,7 +110,7 @@ nfs_free_user_pages(struct page **pages, int npages, int do_dirty)
* nfs_direct_read_seg - Read in one iov segment. Generate separate
* read RPCs for each "rsize" bytes.
* @inode: target inode
* @file: target file (may be NULL)
* @ctx: target file open context
* user_addr: starting address of this segment of user's buffer
* count: size of this segment
* file_offset: offset in file to begin the operation
......@@ -118,7 +118,7 @@ nfs_free_user_pages(struct page **pages, int npages, int do_dirty)
* nr_pages: size of pages array
*/
static int
nfs_direct_read_seg(struct inode *inode, struct file *file,
nfs_direct_read_seg(struct inode *inode, struct nfs_open_context *ctx,
unsigned long user_addr, size_t count, loff_t file_offset,
struct page **pages, int nr_pages)
{
......@@ -127,9 +127,10 @@ nfs_direct_read_seg(struct inode *inode, struct file *file,
int curpage = 0;
struct nfs_read_data rdata = {
.inode = inode,
.cred = ctx->cred,
.args = {
.fh = NFS_FH(inode),
.lockowner = current->files,
.context = ctx,
},
.res = {
.fattr = &rdata.fattr,
......@@ -151,7 +152,7 @@ nfs_direct_read_seg(struct inode *inode, struct file *file,
user_addr + tot_bytes, rdata.args.pgbase, curpage);
lock_kernel();
result = NFS_PROTO(inode)->read(&rdata, file);
result = NFS_PROTO(inode)->read(&rdata);
unlock_kernel();
if (result <= 0) {
......@@ -183,7 +184,7 @@ nfs_direct_read_seg(struct inode *inode, struct file *file,
* nfs_direct_read - For each iov segment, map the user's buffer
* then generate read RPCs.
* @inode: target inode
* @file: target file (may be NULL)
* @ctx: target file open context
* @iov: array of vectors that define I/O buffer
* file_offset: offset in file to begin the operation
* nr_segs: size of iovec array
......@@ -193,7 +194,7 @@ nfs_direct_read_seg(struct inode *inode, struct file *file,
* server.
*/
static ssize_t
nfs_direct_read(struct inode *inode, struct file *file,
nfs_direct_read(struct inode *inode, struct nfs_open_context *ctx,
const struct iovec *iov, loff_t file_offset,
unsigned long nr_segs)
{
......@@ -216,7 +217,7 @@ nfs_direct_read(struct inode *inode, struct file *file,
return page_count;
}
result = nfs_direct_read_seg(inode, file, user_addr, size,
result = nfs_direct_read_seg(inode, ctx, user_addr, size,
file_offset, pages, page_count);
nfs_free_user_pages(pages, page_count, 1);
......@@ -239,7 +240,7 @@ nfs_direct_read(struct inode *inode, struct file *file,
* nfs_direct_write_seg - Write out one iov segment. Generate separate
* write RPCs for each "wsize" bytes, then commit.
* @inode: target inode
* @file: target file (may be NULL)
* @ctx: target file open context
* user_addr: starting address of this segment of user's buffer
* count: size of this segment
* file_offset: offset in file to begin the operation
......@@ -247,7 +248,7 @@ nfs_direct_read(struct inode *inode, struct file *file,
* nr_pages: size of pages array
*/
static int
nfs_direct_write_seg(struct inode *inode, struct file *file,
nfs_direct_write_seg(struct inode *inode, struct nfs_open_context *ctx,
unsigned long user_addr, size_t count, loff_t file_offset,
struct page **pages, int nr_pages)
{
......@@ -257,9 +258,10 @@ nfs_direct_write_seg(struct inode *inode, struct file *file,
struct nfs_writeverf first_verf;
struct nfs_write_data wdata = {
.inode = inode,
.cred = ctx->cred,
.args = {
.fh = NFS_FH(inode),
.lockowner = current->files,
.context = ctx,
},
.res = {
.fattr = &wdata.fattr,
......@@ -290,7 +292,7 @@ nfs_direct_write_seg(struct inode *inode, struct file *file,
user_addr + tot_bytes, wdata.args.pgbase, curpage);
lock_kernel();
result = NFS_PROTO(inode)->write(&wdata, file);
result = NFS_PROTO(inode)->write(&wdata);
unlock_kernel();
if (result <= 0) {
......@@ -325,7 +327,7 @@ nfs_direct_write_seg(struct inode *inode, struct file *file,
wdata.args.offset = file_offset;
lock_kernel();
result = NFS_PROTO(inode)->commit(&wdata, file);
result = NFS_PROTO(inode)->commit(&wdata);
unlock_kernel();
if (result < 0 || memcmp(&first_verf.verifier,
......@@ -349,7 +351,7 @@ nfs_direct_write_seg(struct inode *inode, struct file *file,
* nfs_direct_write - For each iov segment, map the user's buffer
* then generate write and commit RPCs.
* @inode: target inode
* @file: target file (may be NULL)
* @ctx: target file open context
* @iov: array of vectors that define I/O buffer
* file_offset: offset in file to begin the operation
* nr_segs: size of iovec array
......@@ -358,8 +360,7 @@ nfs_direct_write_seg(struct inode *inode, struct file *file,
* that non-direct readers might access, so they will pick up these
* writes immediately.
*/
static ssize_t
nfs_direct_write(struct inode *inode, struct file *file,
static int nfs_direct_write(struct inode *inode, struct nfs_open_context *ctx,
const struct iovec *iov, loff_t file_offset,
unsigned long nr_segs)
{
......@@ -382,7 +383,7 @@ nfs_direct_write(struct inode *inode, struct file *file,
return page_count;
}
result = nfs_direct_write_seg(inode, file, user_addr, size,
result = nfs_direct_write_seg(inode, ctx, user_addr, size,
file_offset, pages, page_count);
nfs_free_user_pages(pages, page_count, 0);
......@@ -414,6 +415,7 @@ nfs_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov,
{
ssize_t result = -EINVAL;
struct file *file = iocb->ki_filp;
struct nfs_open_context *ctx;
struct dentry *dentry = file->f_dentry;
struct inode *inode = dentry->d_inode;
......@@ -423,19 +425,20 @@ nfs_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov,
if (!is_sync_kiocb(iocb))
return result;
ctx = (struct nfs_open_context *)file->private_data;
switch (rw) {
case READ:
dprintk("NFS: direct_IO(read) (%s) off/no(%Lu/%lu)\n",
dentry->d_name.name, file_offset, nr_segs);
result = nfs_direct_read(inode, file, iov,
result = nfs_direct_read(inode, ctx, iov,
file_offset, nr_segs);
break;
case WRITE:
dprintk("NFS: direct_IO(write) (%s) off/no(%Lu/%lu)\n",
dentry->d_name.name, file_offset, nr_segs);
result = nfs_direct_write(inode, file, iov,
result = nfs_direct_write(inode, ctx, iov,
file_offset, nr_segs);
break;
default:
......@@ -471,6 +474,8 @@ nfs_file_direct_read(struct kiocb *iocb, char __user *buf, size_t count, loff_t
ssize_t retval = -EINVAL;
loff_t *ppos = &iocb->ki_pos;
struct file *file = iocb->ki_filp;
struct nfs_open_context *ctx =
(struct nfs_open_context *) file->private_data;
struct dentry *dentry = file->f_dentry;
struct address_space *mapping = file->f_mapping;
struct inode *inode = mapping->host;
......@@ -502,7 +507,7 @@ nfs_file_direct_read(struct kiocb *iocb, char __user *buf, size_t count, loff_t
goto out;
}
retval = nfs_direct_read(inode, file, &iov, pos, 1);
retval = nfs_direct_read(inode, ctx, &iov, pos, 1);
if (retval > 0)
*ppos = pos + retval;
......@@ -542,6 +547,8 @@ nfs_file_direct_write(struct kiocb *iocb, const char __user *buf, size_t count,
loff_t *ppos = &iocb->ki_pos;
unsigned long limit = current->rlim[RLIMIT_FSIZE].rlim_cur;
struct file *file = iocb->ki_filp;
struct nfs_open_context *ctx =
(struct nfs_open_context *) file->private_data;
struct dentry *dentry = file->f_dentry;
struct address_space *mapping = file->f_mapping;
struct inode *inode = mapping->host;
......@@ -589,7 +596,7 @@ nfs_file_direct_write(struct kiocb *iocb, const char __user *buf, size_t count,
goto out;
}
retval = nfs_direct_write(inode, file, &iov, pos, 1);
retval = nfs_direct_write(inode, ctx, &iov, pos, 1);
if (mapping->nrpages)
invalidate_inode_pages2(mapping);
if (retval > 0)
......
......@@ -31,6 +31,8 @@
#include <asm/uaccess.h>
#include <asm/system.h>
#include "delegation.h"
#define NFSDBG_FACILITY NFSDBG_FILE
static int nfs_file_open(struct inode *, struct file *);
......@@ -113,6 +115,7 @@ nfs_file_release(struct inode *inode, struct file *filp)
static int
nfs_file_flush(struct file *file)
{
struct nfs_open_context *ctx = (struct nfs_open_context *)file->private_data;
struct inode *inode = file->f_dentry->d_inode;
int status;
......@@ -124,9 +127,9 @@ nfs_file_flush(struct file *file)
/* Ensure that data+attribute caches are up to date after close() */
status = nfs_wb_all(inode);
if (!status) {
status = file->f_error;
file->f_error = 0;
if (!status)
status = ctx->error;
ctx->error = 0;
if (!status && !nfs_have_delegation(inode, FMODE_READ))
__nfs_revalidate_inode(NFS_SERVER(inode), inode);
}
unlock_kernel();
......@@ -197,6 +200,7 @@ nfs_file_mmap(struct file * file, struct vm_area_struct * vma)
static int
nfs_fsync(struct file *file, struct dentry *dentry, int datasync)
{
struct nfs_open_context *ctx = (struct nfs_open_context *)file->private_data;
struct inode *inode = dentry->d_inode;
int status;
......@@ -205,8 +209,8 @@ nfs_fsync(struct file *file, struct dentry *dentry, int datasync)
lock_kernel();
status = nfs_wb_all(inode);
if (!status) {
status = file->f_error;
file->f_error = 0;
status = ctx->error;
ctx->error = 0;
}
unlock_kernel();
return status;
......
......@@ -39,6 +39,8 @@
#include <asm/system.h>
#include <asm/uaccess.h>
#include "delegation.h"
#define NFSDBG_FACILITY NFSDBG_VFS
#define NFS_PARANOIA 1
......@@ -57,7 +59,6 @@ static struct inode *nfs_alloc_inode(struct super_block *sb);
static void nfs_destroy_inode(struct inode *);
static void nfs_write_inode(struct inode *,int);
static void nfs_delete_inode(struct inode *);
static void nfs_put_super(struct super_block *);
static void nfs_clear_inode(struct inode *);
static void nfs_umount_begin(struct super_block *);
static int nfs_statfs(struct super_block *, struct kstatfs *);
......@@ -68,7 +69,6 @@ static struct super_operations nfs_sops = {
.destroy_inode = nfs_destroy_inode,
.write_inode = nfs_write_inode,
.delete_inode = nfs_delete_inode,
.put_super = nfs_put_super,
.statfs = nfs_statfs,
.clear_inode = nfs_clear_inode,
.umount_begin = nfs_umount_begin,
......@@ -123,8 +123,9 @@ nfs_delete_inode(struct inode * inode)
{
dprintk("NFS: delete_inode(%s/%ld)\n", inode->i_sb->s_id, inode->i_ino);
nfs_wb_all(inode);
/*
* The following can never actually happen...
* The following should never happen...
*/
if (nfs_have_writebacks(inode)) {
printk(KERN_ERR "nfs_delete_inode: inode %ld has pending RPC requests\n", inode->i_ino);
......@@ -141,37 +142,16 @@ static void
nfs_clear_inode(struct inode *inode)
{
struct nfs_inode *nfsi = NFS_I(inode);
struct rpc_cred *cred = nfsi->mm_cred;
struct rpc_cred *cred;
if (cred)
put_rpccred(cred);
nfs_wb_all(inode);
BUG_ON (!list_empty(&nfsi->open_files));
cred = nfsi->cache_access.cred;
if (cred)
put_rpccred(cred);
BUG_ON(atomic_read(&nfsi->data_updates) != 0);
}
void
nfs_put_super(struct super_block *sb)
{
struct nfs_server *server = NFS_SB(sb);
nfs4_renewd_prepare_shutdown(server);
if (server->client != NULL)
rpc_shutdown_client(server->client);
if (server->client_sys != NULL)
rpc_shutdown_client(server->client_sys);
if (!(server->flags & NFS_MOUNT_NONLM))
lockd_down(); /* release rpc.lockd */
rpciod_down(); /* release rpciod */
destroy_nfsv4_state(server);
kfree(server->hostname);
}
void
nfs_umount_begin(struct super_block *sb)
{
......@@ -293,14 +273,6 @@ nfs_sb_init(struct super_block *sb, rpc_authflavor_t authflavor)
server->rsize = nfs_block_size(fsinfo.rtpref, NULL);
if (server->wsize == 0)
server->wsize = nfs_block_size(fsinfo.wtpref, NULL);
if (sb->s_blocksize == 0) {
if (fsinfo.wtmult == 0) {
sb->s_blocksize = 512;
sb->s_blocksize_bits = 9;
} else
sb->s_blocksize = nfs_block_bits(fsinfo.wtmult,
&sb->s_blocksize_bits);
}
if (fsinfo.rtmax >= 512 && server->rsize > fsinfo.rtmax)
server->rsize = nfs_block_size(fsinfo.rtmax, NULL);
......@@ -319,6 +291,11 @@ nfs_sb_init(struct super_block *sb, rpc_authflavor_t authflavor)
server->wsize = server->wpages << PAGE_CACHE_SHIFT;
}
if (sb->s_blocksize == 0)
sb->s_blocksize = nfs_block_bits(server->wsize,
&sb->s_blocksize_bits);
server->wtmult = nfs_block_bits(fsinfo.wtmult, NULL);
server->dtsize = nfs_block_size(fsinfo.dtpref, NULL);
if (server->dtsize > PAGE_CACHE_SIZE)
server->dtsize = PAGE_CACHE_SIZE;
......@@ -405,7 +382,6 @@ static int
nfs_fill_super(struct super_block *sb, struct nfs_mount_data *data, int silent)
{
struct nfs_server *server;
int err = -EIO;
rpc_authflavor_t authflavor;
server = NFS_SB(sb);
......@@ -424,10 +400,14 @@ nfs_fill_super(struct super_block *sb, struct nfs_mount_data *data, int silent)
server->acdirmin = data->acdirmin*HZ;
server->acdirmax = data->acdirmax*HZ;
/* Start lockd here, before we might error out */
if (!(server->flags & NFS_MOUNT_NONLM))
lockd_up();
server->namelen = data->namlen;
server->hostname = kmalloc(strlen(data->hostname) + 1, GFP_KERNEL);
if (!server->hostname)
goto out_fail;
return -ENOMEM;
strcpy(server->hostname, data->hostname);
/* Check NFS protocol revision and initialize RPC op vector
......@@ -438,11 +418,11 @@ nfs_fill_super(struct super_block *sb, struct nfs_mount_data *data, int silent)
server->caps |= NFS_CAP_READDIRPLUS;
if (data->version < 4) {
printk(KERN_NOTICE "NFS: NFSv3 not supported by mount program.\n");
goto out_fail;
return -EIO;
}
#else
printk(KERN_NOTICE "NFS: NFSv3 not supported.\n");
goto out_fail;
return -EIO;
#endif
} else {
server->rpc_ops = &nfs_v2_clientops;
......@@ -457,30 +437,19 @@ nfs_fill_super(struct super_block *sb, struct nfs_mount_data *data, int silent)
/* Create RPC client handles */
server->client = nfs_create_client(server, data);
if (IS_ERR(server->client))
goto out_fail;
return PTR_ERR(server->client);
/* RFC 2623, sec 2.3.2 */
if (authflavor != RPC_AUTH_UNIX) {
server->client_sys = rpc_clone_client(server->client);
if (server->client_sys == NULL)
goto out_shutdown;
if (IS_ERR(server->client_sys))
return PTR_ERR(server->client_sys);
if (!rpcauth_create(RPC_AUTH_UNIX, server->client_sys))
goto out_shutdown;
return -ENOMEM;
} else {
atomic_inc(&server->client->cl_count);
server->client_sys = server->client;
}
/* Fire up rpciod if not yet running */
if (rpciod_up() != 0) {
printk(KERN_WARNING "NFS: couldn't start rpciod!\n");
goto out_shutdown;
}
sb->s_op = &nfs_sops;
err = nfs_sb_init(sb, authflavor);
if (err != 0)
goto out_noinit;
if (server->flags & NFS_MOUNT_VER3) {
if (server->namelen == 0 || server->namelen > NFS3_MAXNAMLEN)
server->namelen = NFS3_MAXNAMLEN;
......@@ -489,21 +458,8 @@ nfs_fill_super(struct super_block *sb, struct nfs_mount_data *data, int silent)
server->namelen = NFS2_MAXNAMLEN;
}
/* Check whether to start the lockd process */
if (!(server->flags & NFS_MOUNT_NONLM))
lockd_up();
return 0;
out_noinit:
rpciod_down();
out_shutdown:
if (server->client)
rpc_shutdown_client(server->client);
if (server->client_sys)
rpc_shutdown_client(server->client_sys);
out_fail:
if (server->hostname)
kfree(server->hostname);
return err;
sb->s_op = &nfs_sops;
return nfs_sb_init(sb, authflavor);
}
static int
......@@ -526,6 +482,7 @@ nfs_statfs(struct super_block *sb, struct kstatfs *buf)
if (error < 0)
goto out_err;
buf->f_frsize = server->wtmult;
buf->f_bsize = sb->s_blocksize;
blockbits = sb->s_blocksize_bits;
blockres = (1 << blockbits) - 1;
......@@ -642,7 +599,7 @@ nfs_find_actor(struct inode *inode, void *opaque)
if (NFS_FILEID(inode) != fattr->fileid)
return 0;
if (memcmp(NFS_FH(inode), fh, sizeof(struct nfs_fh)) != 0)
if (nfs_compare_fh(NFS_FH(inode), fh))
return 0;
if (is_bad_inode(inode))
return 0;
......@@ -653,11 +610,10 @@ static int
nfs_init_locked(struct inode *inode, void *opaque)
{
struct nfs_find_desc *desc = (struct nfs_find_desc *)opaque;
struct nfs_fh *fh = desc->fh;
struct nfs_fattr *fattr = desc->fattr;
NFS_FILEID(inode) = fattr->fileid;
memcpy(NFS_FH(inode), fh, sizeof(struct nfs_fh));
nfs_copy_fh(NFS_FH(inode), desc->fh);
return 0;
}
......@@ -859,53 +815,114 @@ int nfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
return err;
}
struct nfs_open_context *alloc_nfs_open_context(struct dentry *dentry, struct rpc_cred *cred)
{
struct nfs_open_context *ctx;
ctx = (struct nfs_open_context *)kmalloc(sizeof(*ctx), GFP_KERNEL);
if (ctx != NULL) {
atomic_set(&ctx->count, 1);
ctx->dentry = dget(dentry);
ctx->cred = get_rpccred(cred);
ctx->state = NULL;
ctx->lockowner = current->files;
ctx->error = 0;
init_waitqueue_head(&ctx->waitq);
}
return ctx;
}
struct nfs_open_context *get_nfs_open_context(struct nfs_open_context *ctx)
{
if (ctx != NULL)
atomic_inc(&ctx->count);
return ctx;
}
void put_nfs_open_context(struct nfs_open_context *ctx)
{
if (atomic_dec_and_test(&ctx->count)) {
if (ctx->state != NULL)
nfs4_close_state(ctx->state, ctx->mode);
if (ctx->cred != NULL)
put_rpccred(ctx->cred);
dput(ctx->dentry);
kfree(ctx);
}
}
/*
* Ensure that mmap has a recent RPC credential for use when writing out
* shared pages
*/
void
nfs_set_mmcred(struct inode *inode, struct rpc_cred *cred)
void nfs_file_set_open_context(struct file *filp, struct nfs_open_context *ctx)
{
struct rpc_cred **p = &NFS_I(inode)->mm_cred,
*oldcred = *p;
struct inode *inode = filp->f_dentry->d_inode;
struct nfs_inode *nfsi = NFS_I(inode);
*p = get_rpccred(cred);
if (oldcred)
put_rpccred(oldcred);
filp->private_data = get_nfs_open_context(ctx);
spin_lock(&inode->i_lock);
list_add(&ctx->list, &nfsi->open_files);
spin_unlock(&inode->i_lock);
}
struct nfs_open_context *nfs_find_open_context(struct inode *inode, int mode)
{
struct nfs_inode *nfsi = NFS_I(inode);
struct nfs_open_context *pos, *ctx = NULL;
spin_lock(&inode->i_lock);
list_for_each_entry(pos, &nfsi->open_files, list) {
if ((pos->mode & mode) == mode) {
ctx = get_nfs_open_context(pos);
break;
}
}
spin_unlock(&inode->i_lock);
return ctx;
}
void nfs_file_clear_open_context(struct file *filp)
{
struct inode *inode = filp->f_dentry->d_inode;
struct nfs_open_context *ctx = (struct nfs_open_context *)filp->private_data;
if (ctx) {
filp->private_data = NULL;
spin_lock(&inode->i_lock);
list_del(&ctx->list);
spin_unlock(&inode->i_lock);
put_nfs_open_context(ctx);
}
}
/*
* These are probably going to contain hooks for
* allocating and releasing RPC credentials for
* the file. I'll have to think about Tronds patch
* a bit more..
* These allocate and release file read/write context information.
*/
int nfs_open(struct inode *inode, struct file *filp)
{
struct rpc_auth *auth;
struct nfs_open_context *ctx;
struct rpc_cred *cred;
auth = NFS_CLIENT(inode)->cl_auth;
cred = rpcauth_lookupcred(auth, 0);
filp->private_data = cred;
if ((filp->f_mode & FMODE_WRITE) != 0) {
nfs_set_mmcred(inode, cred);
if ((cred = rpcauth_lookupcred(NFS_CLIENT(inode)->cl_auth, 0)) == NULL)
return -ENOMEM;
ctx = alloc_nfs_open_context(filp->f_dentry, cred);
put_rpccred(cred);
if (ctx == NULL)
return -ENOMEM;
ctx->mode = filp->f_mode;
nfs_file_set_open_context(filp, ctx);
put_nfs_open_context(ctx);
if ((filp->f_mode & FMODE_WRITE) != 0)
nfs_begin_data_update(inode);
}
return 0;
}
int nfs_release(struct inode *inode, struct file *filp)
{
struct rpc_cred *cred;
lock_kernel();
if ((filp->f_mode & FMODE_WRITE) != 0)
nfs_end_data_update(inode);
cred = nfs_file_cred(filp);
if (cred)
put_rpccred(cred);
unlock_kernel();
nfs_file_clear_open_context(filp);
return 0;
}
......@@ -946,7 +963,7 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
/* Protect against RPC races by saving the change attribute */
verifier = nfs_save_change_attribute(inode);
status = NFS_PROTO(inode)->getattr(inode, &fattr);
status = NFS_PROTO(inode)->getattr(server, NFS_FH(inode), &fattr);
if (status) {
dfprintk(PAGECACHE, "nfs_revalidate_inode: (%s/%Ld) getattr failed, error=%d\n",
inode->i_sb->s_id,
......@@ -1002,6 +1019,30 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
return status;
}
int nfs_attribute_timeout(struct inode *inode)
{
struct nfs_inode *nfsi = NFS_I(inode);
if (nfs_have_delegation(inode, FMODE_READ))
return 0;
return time_after(jiffies, nfsi->read_cache_jiffies+nfsi->attrtimeo);
}
/**
* nfs_revalidate_inode - Revalidate the inode attributes
* @server - pointer to nfs_server struct
* @inode - pointer to inode struct
*
* Updates inode attribute information by retrieving the data from the server.
*/
int nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
{
if (!(NFS_FLAGS(inode) & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA))
&& !nfs_attribute_timeout(inode))
return NFS_STALE(inode) ? -ESTALE : 0;
return __nfs_revalidate_inode(server, inode);
}
/**
* nfs_begin_data_update
* @inode - pointer to inode
......@@ -1023,11 +1064,13 @@ void nfs_end_data_update(struct inode *inode)
{
struct nfs_inode *nfsi = NFS_I(inode);
/* Mark the attribute cache for revalidation */
nfsi->flags |= NFS_INO_INVALID_ATTR;
/* Directories and symlinks: invalidate page cache too */
if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))
nfsi->flags |= NFS_INO_INVALID_DATA;
if (!nfs_have_delegation(inode, FMODE_READ)) {
/* Mark the attribute cache for revalidation */
nfsi->flags |= NFS_INO_INVALID_ATTR;
/* Directories and symlinks: invalidate page cache too */
if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))
nfsi->flags |= NFS_INO_INVALID_DATA;
}
nfsi->cache_change_attribute ++;
atomic_dec(&nfsi->data_updates);
}
......@@ -1068,6 +1111,10 @@ int nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
loff_t cur_size, new_isize;
int data_unstable;
/* Do we hold a delegation? */
if (nfs_have_delegation(inode, FMODE_READ))
return 0;
/* Are we in the process of updating data on the server? */
data_unstable = nfs_caches_unstable(inode);
......@@ -1265,7 +1312,8 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr, unsign
if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)
|| S_ISLNK(inode->i_mode)))
invalid &= ~NFS_INO_INVALID_DATA;
nfsi->flags |= invalid;
if (!nfs_have_delegation(inode, FMODE_READ))
nfsi->flags |= invalid;
return 0;
out_changed:
......@@ -1305,7 +1353,7 @@ static int nfs_compare_super(struct super_block *sb, void *data)
return 0;
if (old->addr.sin_port != server->addr.sin_port)
return 0;
return !memcmp(&old->fh, &server->fh, sizeof(struct nfs_fh));
return !nfs_compare_fh(&old->fh, &server->fh);
}
static struct super_block *nfs_get_sb(struct file_system_type *fs_type,
......@@ -1330,9 +1378,7 @@ static struct super_block *nfs_get_sb(struct file_system_type *fs_type,
init_nfsv4_state(server);
root = &server->fh;
memcpy(root, &data->root, sizeof(*root));
if (root->size < sizeof(root->data))
memset(root->data+root->size, 0, sizeof(root->data)-root->size);
nfs_copy_fh(root, (struct nfs_fh *) &data->root);
if (data->version != NFS_MOUNT_VERSION) {
printk("nfs warning: mount version %s than kernel\n",
......@@ -1343,7 +1389,6 @@ static struct super_block *nfs_get_sb(struct file_system_type *fs_type,
data->bsize = 0;
if (data->version < 4) {
data->flags &= ~NFS_MOUNT_VER3;
memset(root, 0, sizeof(*root));
root->size = NFS2_FHSIZE;
memcpy(root->data, data->old_root.data, NFS2_FHSIZE);
}
......@@ -1373,6 +1418,13 @@ static struct super_block *nfs_get_sb(struct file_system_type *fs_type,
s->s_flags = flags;
/* Fire up rpciod if not yet running */
if (rpciod_up() != 0) {
printk(KERN_WARNING "NFS: couldn't start rpciod!\n");
kfree(server);
return ERR_PTR(-EIO);
}
error = nfs_fill_super(s, data, flags & MS_VERBOSE ? 1 : 0);
if (error) {
up_write(&s->s_umount);
......@@ -1386,7 +1438,25 @@ static struct super_block *nfs_get_sb(struct file_system_type *fs_type,
static void nfs_kill_super(struct super_block *s)
{
struct nfs_server *server = NFS_SB(s);
kill_anon_super(s);
nfs4_renewd_prepare_shutdown(server);
if (server->client != NULL && !IS_ERR(server->client))
rpc_shutdown_client(server->client);
if (server->client_sys != NULL && !IS_ERR(server->client_sys))
rpc_shutdown_client(server->client_sys);
if (!(server->flags & NFS_MOUNT_NONLM))
lockd_down(); /* release rpc.lockd */
rpciod_down(); /* release rpciod */
destroy_nfsv4_state(server);
if (server->hostname != NULL)
kfree(server->hostname);
kfree(server);
}
......@@ -1402,12 +1472,12 @@ static struct file_system_type nfs_fs_type = {
static void nfs4_clear_inode(struct inode *);
static struct super_operations nfs4_sops = {
.alloc_inode = nfs_alloc_inode,
.destroy_inode = nfs_destroy_inode,
.write_inode = nfs_write_inode,
.delete_inode = nfs_delete_inode,
.put_super = nfs_put_super,
.statfs = nfs_statfs,
.clear_inode = nfs4_clear_inode,
.umount_begin = nfs_umount_begin,
......@@ -1423,6 +1493,12 @@ static void nfs4_clear_inode(struct inode *inode)
{
struct nfs_inode *nfsi = NFS_I(inode);
/* If we are holding a delegation, return it! */
if (nfsi->delegation != NULL)
nfs_inode_return_delegation(inode);
/* First call standard NFS clear_inode() code */
nfs_clear_inode(inode);
/* Now clear out any remaining state */
while (!list_empty(&nfsi->open_states)) {
struct nfs4_state *state;
......@@ -1437,8 +1513,6 @@ static void nfs4_clear_inode(struct inode *inode)
BUG_ON(atomic_read(&state->count) != 1);
nfs4_close_state(state, state->state);
}
/* Now call standard NFS clear_inode() code */
nfs_clear_inode(inode);
}
......@@ -1498,7 +1572,7 @@ static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data,
clp = nfs4_get_client(&server->addr.sin_addr);
if (!clp) {
printk(KERN_WARNING "NFS: failed to create NFS4 client.\n");
goto out_fail;
return -EIO;
}
/* Now create transport and client */
......@@ -1536,8 +1610,13 @@ static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data,
memcpy(clp->cl_ipaddr, server->ip_addr, sizeof(clp->cl_ipaddr));
nfs_idmap_new(clp);
}
if (list_empty(&clp->cl_superblocks))
clear_bit(NFS4CLNT_OK, &clp->cl_state);
if (list_empty(&clp->cl_superblocks)) {
err = nfs4_init_client(clp);
if (err != 0) {
up_write(&clp->cl_sem);
goto out_fail;
}
}
list_add_tail(&server->nfs4_siblings, &clp->cl_superblocks);
clnt = rpc_clone_client(clp->cl_rpcclient);
if (!IS_ERR(clnt))
......@@ -1547,45 +1626,29 @@ static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data,
if (IS_ERR(clnt)) {
printk(KERN_WARNING "NFS: cannot create RPC client.\n");
err = PTR_ERR(clnt);
goto out_remove_list;
return PTR_ERR(clnt);
}
clnt->cl_intr = (server->flags & NFS4_MOUNT_INTR) ? 1 : 0;
clnt->cl_softrtry = (server->flags & NFS4_MOUNT_SOFT) ? 1 : 0;
server->client = clnt;
err = -ENOMEM;
if (server->nfs4_state->cl_idmap == NULL) {
printk(KERN_WARNING "NFS: failed to create idmapper.\n");
goto out_shutdown;
return -ENOMEM;
}
if (clnt->cl_auth->au_flavor != authflavour) {
if (rpcauth_create(authflavour, clnt) == NULL) {
printk(KERN_WARNING "NFS: couldn't create credcache!\n");
goto out_shutdown;
return -ENOMEM;
}
}
/* Fire up rpciod if not yet running */
if (rpciod_up() != 0) {
printk(KERN_WARNING "NFS: couldn't start rpciod!\n");
goto out_shutdown;
}
sb->s_op = &nfs4_sops;
err = nfs_sb_init(sb, authflavour);
if (err == 0)
return 0;
rpciod_down();
out_shutdown:
rpc_shutdown_client(server->client);
out_remove_list:
down_write(&server->nfs4_state->cl_sem);
list_del_init(&server->nfs4_siblings);
up_write(&server->nfs4_state->cl_sem);
destroy_nfsv4_state(server);
out_fail:
if (clp)
nfs4_put_client(clp);
......@@ -1691,6 +1754,13 @@ static struct super_block *nfs4_get_sb(struct file_system_type *fs_type,
s->s_flags = flags;
/* Fire up rpciod if not yet running */
if (rpciod_up() != 0) {
printk(KERN_WARNING "NFS: couldn't start rpciod!\n");
s = ERR_PTR(-EIO);
goto out_free;
}
error = nfs4_fill_super(s, data, flags & MS_VERBOSE ? 1 : 0);
if (error) {
up_write(&s->s_umount);
......@@ -1710,22 +1780,31 @@ static struct super_block *nfs4_get_sb(struct file_system_type *fs_type,
return s;
}
static void nfs4_kill_super(struct super_block *sb)
{
nfs_return_all_delegations(sb);
nfs_kill_super(sb);
}
static struct file_system_type nfs4_fs_type = {
.owner = THIS_MODULE,
.name = "nfs4",
.get_sb = nfs4_get_sb,
.kill_sb = nfs_kill_super,
.kill_sb = nfs4_kill_super,
.fs_flags = FS_ODD_RENAME|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
};
#define nfs4_zero_state(nfsi) \
#define nfs4_init_once(nfsi) \
do { \
INIT_LIST_HEAD(&(nfsi)->open_states); \
nfsi->delegation = NULL; \
nfsi->delegation_state = 0; \
init_rwsem(&nfsi->rwsem); \
} while(0)
#define register_nfs4fs() register_filesystem(&nfs4_fs_type)
#define unregister_nfs4fs() unregister_filesystem(&nfs4_fs_type)
#else
#define nfs4_zero_state(nfsi) \
#define nfs4_init_once(nfsi) \
do { } while (0)
#define register_nfs4fs() (0)
#define unregister_nfs4fs()
......@@ -1747,8 +1826,6 @@ static struct inode *nfs_alloc_inode(struct super_block *sb)
if (!nfsi)
return NULL;
nfsi->flags = 0;
nfsi->mm_cred = NULL;
nfs4_zero_state(nfsi);
return &nfsi->vfs_inode;
}
......@@ -1764,14 +1841,17 @@ static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
SLAB_CTOR_CONSTRUCTOR) {
inode_init_once(&nfsi->vfs_inode);
spin_lock_init(&nfsi->req_lock);
INIT_LIST_HEAD(&nfsi->dirty);
INIT_LIST_HEAD(&nfsi->commit);
INIT_LIST_HEAD(&nfsi->open_files);
INIT_RADIX_TREE(&nfsi->nfs_page_tree, GFP_ATOMIC);
atomic_set(&nfsi->data_updates, 0);
nfsi->ndirty = 0;
nfsi->ncommit = 0;
nfsi->npages = 0;
init_waitqueue_head(&nfsi->nfs_i_wait);
nfs4_init_once(nfsi);
}
}
......
......@@ -108,7 +108,6 @@ xdr_decode_fhstatus(struct rpc_rqst *req, u32 *p, struct mnt_fhstatus *res)
{
struct nfs_fh *fh = res->fh;
memset((void *)fh, 0, sizeof(*fh));
if ((res->status = ntohl(*p++)) == 0) {
fh->size = NFS2_FHSIZE;
memcpy(fh->data, p, NFS2_FHSIZE);
......@@ -121,7 +120,6 @@ xdr_decode_fhstatus3(struct rpc_rqst *req, u32 *p, struct mnt_fhstatus *res)
{
struct nfs_fh *fh = res->fh;
memset((void *)fh, 0, sizeof(*fh));
if ((res->status = ntohl(*p++)) == 0) {
int size = ntohl(*p++);
if (size <= NFS3_FHSIZE) {
......
......@@ -77,8 +77,6 @@ xdr_encode_fhandle(u32 *p, struct nfs_fh *fhandle)
static inline u32 *
xdr_decode_fhandle(u32 *p, struct nfs_fh *fhandle)
{
/* Zero handle first to allow comparisons */
memset(fhandle, 0, sizeof(*fhandle));
/* NFSv2 handles have a fixed length */
fhandle->size = NFS2_FHSIZE;
memcpy(fhandle->data, p, NFS2_FHSIZE);
......@@ -94,6 +92,23 @@ xdr_encode_time(u32 *p, struct timespec *timep)
return p;
}
static inline u32*
xdr_encode_current_server_time(u32 *p, struct timespec *timep)
{
/*
* Passing the invalid value useconds=1000000 is a
* Sun convention for "set to current server time".
* It's needed to make permissions checks for the
* "touch" program across v2 mounts to Solaris and
* Irix boxes work correctly. See description of
* sattr in section 6.1 of "NFS Illustrated" by
* Brent Callaghan, Addison-Wesley, ISBN 0-201-32750-5
*/
*p++ = htonl(timep->tv_sec);
*p++ = htonl(1000000);
return p;
}
static inline u32*
xdr_decode_time(u32 *p, struct timespec *timep)
{
......@@ -142,15 +157,19 @@ xdr_encode_sattr(u32 *p, struct iattr *attr)
SATTR(p, attr, ATTR_GID, ia_gid);
SATTR(p, attr, ATTR_SIZE, ia_size);
if (attr->ia_valid & (ATTR_ATIME|ATTR_ATIME_SET)) {
if (attr->ia_valid & ATTR_ATIME_SET) {
p = xdr_encode_time(p, &attr->ia_atime);
} else if (attr->ia_valid & ATTR_ATIME) {
p = xdr_encode_current_server_time(p, &attr->ia_atime);
} else {
*p++ = ~(u32) 0;
*p++ = ~(u32) 0;
}
if (attr->ia_valid & (ATTR_MTIME|ATTR_MTIME_SET)) {
if (attr->ia_valid & ATTR_MTIME_SET) {
p = xdr_encode_time(p, &attr->ia_mtime);
} else if (attr->ia_valid & ATTR_MTIME) {
p = xdr_encode_current_server_time(p, &attr->ia_mtime);
} else {
*p++ = ~(u32) 0;
*p++ = ~(u32) 0;
......
......@@ -68,18 +68,6 @@ nfs3_async_handle_jukebox(struct rpc_task *task)
return 1;
}
static struct rpc_cred *
nfs_cred(struct inode *inode, struct file *filp)
{
struct rpc_cred *cred = NULL;
if (filp)
cred = (struct rpc_cred *)filp->private_data;
if (!cred)
cred = NFS_I(inode)->mm_cred;
return cred;
}
/*
* Bare-bones access to getattr: this is for nfs_read_super.
*/
......@@ -104,14 +92,15 @@ nfs3_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
* One function for each procedure in the NFS protocol.
*/
static int
nfs3_proc_getattr(struct inode *inode, struct nfs_fattr *fattr)
nfs3_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
struct nfs_fattr *fattr)
{
int status;
dprintk("NFS call getattr\n");
fattr->valid = 0;
status = rpc_call(NFS_CLIENT(inode), NFS3PROC_GETATTR,
NFS_FH(inode), fattr, 0);
status = rpc_call(server->client, NFS3PROC_GETATTR,
fhandle, fattr, 0);
dprintk("NFS reply getattr\n");
return status;
}
......@@ -164,8 +153,7 @@ nfs3_proc_lookup(struct inode *dir, struct qstr *name,
return status;
}
static int
nfs3_proc_access(struct inode *inode, struct rpc_cred *cred, int mode)
static int nfs3_proc_access(struct inode *inode, struct nfs_access_entry *entry)
{
struct nfs_fattr fattr;
struct nfs3_accessargs arg = {
......@@ -178,9 +166,10 @@ nfs3_proc_access(struct inode *inode, struct rpc_cred *cred, int mode)
.rpc_proc = &nfs3_procedures[NFS3PROC_ACCESS],
.rpc_argp = &arg,
.rpc_resp = &res,
.rpc_cred = cred
.rpc_cred = entry->cred
};
int status;
int mode = entry->mask;
int status;
dprintk("NFS call access\n");
fattr.valid = 0;
......@@ -200,10 +189,16 @@ nfs3_proc_access(struct inode *inode, struct rpc_cred *cred, int mode)
}
status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
nfs_refresh_inode(inode, &fattr);
dprintk("NFS reply access\n");
if (status == 0 && (arg.access & res.access) != arg.access)
status = -EACCES;
if (status == 0) {
entry->mask = 0;
if (res.access & NFS3_ACCESS_READ)
entry->mask |= MAY_READ;
if (res.access & (NFS3_ACCESS_MODIFY | NFS3_ACCESS_EXTEND | NFS3_ACCESS_DELETE))
entry->mask |= MAY_WRITE;
if (res.access & (NFS3_ACCESS_LOOKUP|NFS3_ACCESS_EXECUTE))
entry->mask |= MAY_EXEC;
}
dprintk("NFS reply access, status = %d\n", status);
return status;
}
......@@ -227,8 +222,7 @@ nfs3_proc_readlink(struct inode *inode, struct page *page)
return status;
}
static int
nfs3_proc_read(struct nfs_read_data *rdata, struct file *filp)
static int nfs3_proc_read(struct nfs_read_data *rdata)
{
int flags = rdata->flags;
struct inode * inode = rdata->inode;
......@@ -237,13 +231,13 @@ nfs3_proc_read(struct nfs_read_data *rdata, struct file *filp)
.rpc_proc = &nfs3_procedures[NFS3PROC_READ],
.rpc_argp = &rdata->args,
.rpc_resp = &rdata->res,
.rpc_cred = rdata->cred,
};
int status;
dprintk("NFS call read %d @ %Ld\n", rdata->args.count,
(long long) rdata->args.offset);
fattr->valid = 0;
msg.rpc_cred = nfs_cred(inode, filp);
status = rpc_call_sync(NFS_CLIENT(inode), &msg, flags);
if (status >= 0)
nfs_refresh_inode(inode, fattr);
......@@ -251,8 +245,7 @@ nfs3_proc_read(struct nfs_read_data *rdata, struct file *filp)
return status;
}
static int
nfs3_proc_write(struct nfs_write_data *wdata, struct file *filp)
static int nfs3_proc_write(struct nfs_write_data *wdata)
{
int rpcflags = wdata->flags;
struct inode * inode = wdata->inode;
......@@ -261,13 +254,13 @@ nfs3_proc_write(struct nfs_write_data *wdata, struct file *filp)
.rpc_proc = &nfs3_procedures[NFS3PROC_WRITE],
.rpc_argp = &wdata->args,
.rpc_resp = &wdata->res,
.rpc_cred = wdata->cred,
};
int status;
dprintk("NFS call write %d @ %Ld\n", wdata->args.count,
(long long) wdata->args.offset);
fattr->valid = 0;
msg.rpc_cred = nfs_cred(inode, filp);
status = rpc_call_sync(NFS_CLIENT(inode), &msg, rpcflags);
if (status >= 0)
nfs_refresh_inode(inode, fattr);
......@@ -275,8 +268,7 @@ nfs3_proc_write(struct nfs_write_data *wdata, struct file *filp)
return status < 0? status : wdata->res.count;
}
static int
nfs3_proc_commit(struct nfs_write_data *cdata, struct file *filp)
static int nfs3_proc_commit(struct nfs_write_data *cdata)
{
struct inode * inode = cdata->inode;
struct nfs_fattr * fattr = cdata->res.fattr;
......@@ -284,13 +276,13 @@ nfs3_proc_commit(struct nfs_write_data *cdata, struct file *filp)
.rpc_proc = &nfs3_procedures[NFS3PROC_COMMIT],
.rpc_argp = &cdata->args,
.rpc_resp = &cdata->res,
.rpc_cred = cdata->cred,
};
int status;
dprintk("NFS call commit %d @ %Ld\n", cdata->args.count,
(long long) cdata->args.offset);
fattr->valid = 0;
msg.rpc_cred = nfs_cred(inode, filp);
status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
if (status >= 0)
nfs_refresh_inode(inode, fattr);
......@@ -534,6 +526,8 @@ nfs3_proc_symlink(struct inode *dir, struct qstr *name, struct qstr *path,
};
int status;
if (path->len > NFS3_MAXPATHLEN)
return -ENAMETOOLONG;
dprintk("NFS call symlink %s -> %s\n", name->name, path->name);
dir_attr.valid = 0;
fattr->valid = 0;
......@@ -832,27 +826,6 @@ nfs3_proc_commit_setup(struct nfs_write_data *data, int how)
rpc_call_setup(task, &msg, 0);
}
/*
* Set up the nfspage struct with the right credentials
*/
void
nfs3_request_init(struct nfs_page *req, struct file *filp)
{
req->wb_cred = get_rpccred(nfs_cred(req->wb_inode, filp));
}
static int
nfs3_request_compatible(struct nfs_page *req, struct file *filp, struct page *page)
{
if (req->wb_file != filp)
return 0;
if (req->wb_page != page)
return 0;
if (req->wb_cred != nfs_file_cred(filp))
return 0;
return 1;
}
static int
nfs3_proc_lock(struct file *filp, int cmd, struct file_lock *fl)
{
......@@ -892,7 +865,5 @@ struct nfs_rpc_ops nfs_v3_clientops = {
.commit_setup = nfs3_proc_commit_setup,
.file_open = nfs_open,
.file_release = nfs_release,
.request_init = nfs3_request_init,
.request_compatible = nfs3_request_compatible,
.lock = nfs3_proc_lock,
};
......@@ -109,10 +109,6 @@ xdr_encode_fhandle(u32 *p, struct nfs_fh *fh)
static inline u32 *
xdr_decode_fhandle(u32 *p, struct nfs_fh *fh)
{
/*
* Zero all nonused bytes
*/
memset((u8 *)fh, 0, sizeof(*fh));
if ((fh->size = ntohl(*p++)) <= NFS3_FHSIZE) {
memcpy(fh->data, p, fh->size);
return p + XDR_QUADLEN(fh->size);
......
......@@ -47,12 +47,16 @@
#include <linux/smp_lock.h>
#include <linux/namei.h>
#include "delegation.h"
#define NFSDBG_FACILITY NFSDBG_PROC
#define NFS4_POLL_RETRY_TIME (15*HZ)
#define NFS4_POLL_RETRY_MIN (1*HZ)
#define NFS4_POLL_RETRY_MAX (15*HZ)
static int nfs4_do_fsinfo(struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *);
static int nfs4_async_handle_error(struct rpc_task *, struct nfs_server *);
static int _nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry);
extern u32 *nfs4_decode_dirent(u32 *p, struct nfs_entry *entry, int plus);
extern struct rpc_procinfo nfs4_procedures[];
......@@ -189,53 +193,296 @@ static void update_changeattr(struct inode *inode, struct nfs4_change_info *cinf
* reclaim state on the server after a reboot.
* Assumes caller is holding the sp->so_sem
*/
int
nfs4_open_reclaim(struct nfs4_state_owner *sp, struct nfs4_state *state)
static int _nfs4_open_reclaim(struct nfs4_state_owner *sp, struct nfs4_state *state)
{
struct inode *inode = state->inode;
struct nfs_server *server = NFS_SERVER(inode);
struct nfs_fattr fattr = {
.valid = 0,
};
struct nfs_open_reclaimargs o_arg = {
struct nfs_delegation *delegation = NFS_I(inode)->delegation;
struct nfs_openargs o_arg = {
.fh = NFS_FH(inode),
.seqid = sp->so_seqid,
.id = sp->so_id,
.share_access = state->state,
.open_flags = state->state,
.clientid = server->nfs4_state->cl_clientid,
.claim = NFS4_OPEN_CLAIM_PREVIOUS,
.bitmask = server->attr_bitmask,
};
struct nfs_openres o_res = {
.f_attr = &fattr,
.server = server, /* Grrr */
};
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_RECLAIM],
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_NOATTR],
.rpc_argp = &o_arg,
.rpc_resp = &o_res,
.rpc_cred = sp->so_cred,
};
int status;
status = rpc_call_sync(server->client, &msg, 0);
if (delegation != NULL) {
if (!(delegation->flags & NFS_DELEGATION_NEED_RECLAIM)) {
memcpy(&state->stateid, &delegation->stateid,
sizeof(state->stateid));
set_bit(NFS_DELEGATED_STATE, &state->flags);
return 0;
}
o_arg.u.delegation_type = delegation->type;
}
status = rpc_call_sync(server->client, &msg, RPC_TASK_NOINTR);
nfs4_increment_seqid(status, sp);
if (status == 0)
if (status == 0) {
memcpy(&state->stateid, &o_res.stateid, sizeof(state->stateid));
/* Update the inode attributes */
nfs_refresh_inode(inode, &fattr);
if (o_res.delegation_type != 0) {
nfs_inode_reclaim_delegation(inode, sp->so_cred, &o_res);
/* Did the server issue an immediate delegation recall? */
if (o_res.do_recall)
nfs_async_inode_return_delegation(inode, &o_res.stateid);
}
}
clear_bit(NFS_DELEGATED_STATE, &state->flags);
/* Ensure we update the inode attributes */
NFS_CACHEINV(inode);
return status;
}
int nfs4_open_reclaim(struct nfs4_state_owner *sp, struct nfs4_state *state)
{
struct nfs_server *server = NFS_SERVER(state->inode);
struct nfs4_exception exception = { };
int err;
do {
err = _nfs4_open_reclaim(sp, state);
switch (err) {
case 0:
case -NFS4ERR_STALE_CLIENTID:
case -NFS4ERR_STALE_STATEID:
case -NFS4ERR_EXPIRED:
return err;
}
err = nfs4_handle_exception(server, err, &exception);
} while (exception.retry);
return err;
}
static int _nfs4_open_delegation_recall(struct dentry *dentry, struct nfs4_state *state)
{
struct nfs4_state_owner *sp = state->owner;
struct inode *inode = dentry->d_inode;
struct nfs_server *server = NFS_SERVER(inode);
struct dentry *parent = dget_parent(dentry);
struct nfs_openargs arg = {
.fh = NFS_FH(parent->d_inode),
.clientid = server->nfs4_state->cl_clientid,
.name = &dentry->d_name,
.id = sp->so_id,
.server = server,
.bitmask = server->attr_bitmask,
.claim = NFS4_OPEN_CLAIM_DELEGATE_CUR,
};
struct nfs_openres res = {
.server = server,
};
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_NOATTR],
.rpc_argp = &arg,
.rpc_resp = &res,
.rpc_cred = sp->so_cred,
};
int status = 0;
down(&sp->so_sema);
if (!test_bit(NFS_DELEGATED_STATE, &state->flags))
goto out;
if (state->state == 0)
goto out;
arg.seqid = sp->so_seqid;
arg.open_flags = state->state;
memcpy(arg.u.delegation.data, state->stateid.data, sizeof(arg.u.delegation.data));
status = rpc_call_sync(server->client, &msg, RPC_TASK_NOINTR);
nfs4_increment_seqid(status, sp);
if (status >= 0) {
memcpy(state->stateid.data, res.stateid.data,
sizeof(state->stateid.data));
clear_bit(NFS_DELEGATED_STATE, &state->flags);
}
out:
up(&sp->so_sema);
dput(parent);
return status;
}
int nfs4_open_delegation_recall(struct dentry *dentry, struct nfs4_state *state)
{
struct nfs4_exception exception = { };
struct nfs_server *server = NFS_SERVER(dentry->d_inode);
int err;
do {
err = _nfs4_open_delegation_recall(dentry, state);
switch (err) {
case 0:
return err;
case -NFS4ERR_STALE_CLIENTID:
case -NFS4ERR_STALE_STATEID:
case -NFS4ERR_EXPIRED:
/* Don't recall a delegation if it was lost */
nfs4_schedule_state_recovery(server->nfs4_state);
return err;
}
err = nfs4_handle_exception(server, err, &exception);
} while (exception.retry);
return err;
}
static int _nfs4_proc_open_confirm(struct rpc_clnt *clnt, const struct nfs_fh *fh, struct nfs4_state_owner *sp, nfs4_stateid *stateid)
{
struct nfs_open_confirmargs arg = {
.fh = fh,
.seqid = sp->so_seqid,
.stateid = *stateid,
};
struct nfs_open_confirmres res;
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_CONFIRM],
.rpc_argp = &arg,
.rpc_resp = &res,
.rpc_cred = sp->so_cred,
};
int status;
status = rpc_call_sync(clnt, &msg, RPC_TASK_NOINTR);
nfs4_increment_seqid(status, sp);
if (status >= 0)
memcpy(stateid, &res.stateid, sizeof(*stateid));
return status;
}
static int _nfs4_do_access(struct inode *inode, struct rpc_cred *cred, int mask)
{
struct nfs_access_entry cache;
int status;
status = nfs_access_get_cached(inode, cred, &cache);
if (status == 0)
goto out;
/* Be clever: ask server to check for all possible rights */
cache.mask = MAY_EXEC | MAY_WRITE | MAY_READ;
cache.cred = cred;
cache.jiffies = jiffies;
status = _nfs4_proc_access(inode, &cache);
if (status != 0)
return status;
nfs_access_add_cache(inode, &cache);
out:
if ((cache.mask & mask) == mask)
return 0;
return -EACCES;
}
/*
* Returns an nfs4_state + an extra reference to the inode
*/
int _nfs4_open_delegated(struct inode *inode, int flags, struct rpc_cred *cred, struct nfs4_state **res)
{
struct nfs_delegation *delegation;
struct nfs_server *server = NFS_SERVER(inode);
struct nfs4_client *clp = server->nfs4_state;
struct nfs_inode *nfsi = NFS_I(inode);
struct nfs4_state_owner *sp = NULL;
struct nfs4_state *state = NULL;
int open_flags = flags & (FMODE_READ|FMODE_WRITE);
int mask = 0;
int err;
/* Protect against reboot recovery - NOTE ORDER! */
down_read(&clp->cl_sem);
/* Protect against delegation recall */
down_read(&nfsi->rwsem);
delegation = NFS_I(inode)->delegation;
err = -ENOENT;
if (delegation == NULL || (delegation->type & open_flags) != open_flags)
goto out_err;
err = -ENOMEM;
if (!(sp = nfs4_get_state_owner(server, cred))) {
dprintk("%s: nfs4_get_state_owner failed!\n", __FUNCTION__);
goto out_err;
}
down(&sp->so_sema);
state = nfs4_get_open_state(inode, sp);
if (state == NULL)
goto out_err;
err = -ENOENT;
if ((state->state & open_flags) == open_flags) {
spin_lock(&inode->i_lock);
if (open_flags & FMODE_READ)
state->nreaders++;
if (open_flags & FMODE_WRITE)
state->nwriters++;
spin_unlock(&inode->i_lock);
goto out_ok;
} else if (state->state != 0)
goto out_err;
lock_kernel();
err = _nfs4_do_access(inode, cred, mask);
unlock_kernel();
if (err != 0)
goto out_err;
spin_lock(&inode->i_lock);
memcpy(state->stateid.data, delegation->stateid.data,
sizeof(state->stateid.data));
state->state |= open_flags;
if (open_flags & FMODE_READ)
state->nreaders++;
if (open_flags & FMODE_WRITE)
state->nwriters++;
set_bit(NFS_DELEGATED_STATE, &state->flags);
spin_unlock(&inode->i_lock);
out_ok:
up(&sp->so_sema);
nfs4_put_state_owner(sp);
up_read(&nfsi->rwsem);
up_read(&clp->cl_sem);
igrab(inode);
*res = state;
return 0;
out_err:
if (sp != NULL) {
if (state != NULL)
nfs4_put_open_state(state);
up(&sp->so_sema);
nfs4_put_state_owner(sp);
}
up_read(&nfsi->rwsem);
up_read(&clp->cl_sem);
return err;
}
static struct nfs4_state *nfs4_open_delegated(struct inode *inode, int flags, struct rpc_cred *cred)
{
struct nfs4_exception exception = { };
struct nfs4_state *res;
int err;
do {
err = _nfs4_open_delegated(inode, flags, cred, &res);
if (err == 0)
break;
res = ERR_PTR(nfs4_handle_exception(NFS_SERVER(inode),
err, &exception));
} while (exception.retry);
return res;
}
/*
* Returns an nfs4_state + an referenced inode
*/
struct nfs4_state *
nfs4_do_open(struct inode *dir, struct qstr *name, int flags, struct iattr *sattr, struct rpc_cred *cred)
static int _nfs4_do_open(struct inode *dir, struct qstr *name, int flags, struct iattr *sattr, struct rpc_cred *cred, struct nfs4_state **res)
{
struct nfs4_state_owner *sp;
struct nfs4_state *state = NULL;
struct nfs_server *server = NFS_SERVER(dir);
struct nfs4_client *clp = server->nfs4_state;
struct inode *inode = NULL;
int status;
struct nfs_fattr f_attr = {
......@@ -243,12 +490,11 @@ nfs4_do_open(struct inode *dir, struct qstr *name, int flags, struct iattr *satt
};
struct nfs_openargs o_arg = {
.fh = NFS_FH(dir),
.share_access = flags & (FMODE_READ|FMODE_WRITE),
.opentype = (flags & O_CREAT) ? NFS4_OPEN_CREATE : NFS4_OPEN_NOCREATE,
.createmode = (flags & O_EXCL) ? NFS4_CREATE_EXCLUSIVE : NFS4_CREATE_UNCHECKED,
.open_flags = flags,
.name = name,
.server = server,
.bitmask = server->attr_bitmask,
.claim = NFS4_OPEN_CLAIM_NULL,
};
struct nfs_openres o_res = {
.f_attr = &f_attr,
......@@ -261,60 +507,50 @@ nfs4_do_open(struct inode *dir, struct qstr *name, int flags, struct iattr *satt
.rpc_cred = cred,
};
retry:
/* Protect against reboot recovery conflicts */
down_read(&clp->cl_sem);
status = -ENOMEM;
if (!(sp = nfs4_get_state_owner(NFS_SERVER(dir), cred))) {
if (!(sp = nfs4_get_state_owner(server, cred))) {
dprintk("nfs4_do_open: nfs4_get_state_owner failed!\n");
goto out;
goto out_err;
}
if (o_arg.createmode & NFS4_CREATE_EXCLUSIVE){
if (flags & O_EXCL) {
u32 *p = (u32 *) o_arg.u.verifier.data;
p[0] = jiffies;
p[1] = current->pid;
} else if (o_arg.createmode == NFS4_CREATE_UNCHECKED) {
} else
o_arg.u.attrs = sattr;
}
/* Serialization for the sequence id */
down(&sp->so_sema);
o_arg.seqid = sp->so_seqid;
o_arg.id = sp->so_id;
o_arg.clientid = NFS_SERVER(dir)->nfs4_state->cl_clientid,
o_arg.clientid = clp->cl_clientid,
status = rpc_call_sync(server->client, &msg, 0);
status = rpc_call_sync(server->client, &msg, RPC_TASK_NOINTR);
nfs4_increment_seqid(status, sp);
if (status)
goto out_up;
goto out_err;
update_changeattr(dir, &o_res.cinfo);
if(o_res.rflags & NFS4_OPEN_RESULT_CONFIRM) {
status = _nfs4_proc_open_confirm(server->client, &o_res.fh,
sp, &o_res.stateid);
if (status != 0)
goto out_err;
}
if (!(f_attr.valid & NFS_ATTR_FATTR)) {
status = server->rpc_ops->getattr(server, &o_res.fh, &f_attr);
if (status < 0)
goto out_err;
}
status = -ENOMEM;
inode = nfs_fhget(dir->i_sb, &o_res.fh, &f_attr);
if (!inode)
goto out_up;
goto out_err;
state = nfs4_get_open_state(inode, sp);
if (!state)
goto out_up;
if(o_res.rflags & NFS4_OPEN_RESULT_CONFIRM) {
struct nfs_open_confirmargs oc_arg = {
.fh = &o_res.fh,
.seqid = sp->so_seqid,
};
struct nfs_open_confirmres oc_res;
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_CONFIRM],
.rpc_argp = &oc_arg,
.rpc_resp = &oc_res,
.rpc_cred = cred,
};
memcpy(&oc_arg.stateid, &o_res.stateid, sizeof(oc_arg.stateid));
status = rpc_call_sync(server->client, &msg, 0);
nfs4_increment_seqid(status, sp);
if (status)
goto out_up;
memcpy(&state->stateid, &oc_res.stateid, sizeof(state->stateid));
} else
memcpy(&state->stateid, &o_res.stateid, sizeof(state->stateid));
goto out_err;
memcpy(&state->stateid, &o_res.stateid, sizeof(state->stateid));
spin_lock(&inode->i_lock);
if (flags & FMODE_READ)
state->nreaders++;
......@@ -322,47 +558,62 @@ nfs4_do_open(struct inode *dir, struct qstr *name, int flags, struct iattr *satt
state->nwriters++;
state->state |= flags & (FMODE_READ|FMODE_WRITE);
spin_unlock(&inode->i_lock);
if (o_res.delegation_type != 0)
nfs_inode_set_delegation(inode, cred, &o_res);
up(&sp->so_sema);
nfs4_put_state_owner(sp);
return state;
out_up:
up(&sp->so_sema);
nfs4_put_state_owner(sp);
if (state) {
nfs4_put_open_state(state);
state = NULL;
up_read(&clp->cl_sem);
*res = state;
return 0;
out_err:
if (sp != NULL) {
if (state != NULL)
nfs4_put_open_state(state);
up(&sp->so_sema);
nfs4_put_state_owner(sp);
}
if (inode) {
/* Note: clp->cl_sem must be released before nfs4_put_open_state()! */
up_read(&clp->cl_sem);
if (inode != NULL)
iput(inode);
inode = NULL;
}
/* NOTE: BAD_SEQID means the server and client disagree about the
* book-keeping w.r.t. state-changing operations
* (OPEN/CLOSE/LOCK/LOCKU...)
* It is actually a sign of a bug on the client or on the server.
*
* If we receive a BAD_SEQID error in the particular case of
* doing an OPEN, we assume that nfs4_increment_seqid() will
* have unhashed the old state_owner for us, and that we can
* therefore safely retry using a new one. We should still warn
* the user though...
*/
if (status == -NFS4ERR_BAD_SEQID) {
printk(KERN_WARNING "NFS: v4 server returned a bad sequence-id error!\n");
goto retry;
}
status = nfs4_handle_error(server, status);
if (!status)
goto retry;
BUG_ON(status < -1000 || status > 0);
out:
return ERR_PTR(status);
*res = NULL;
return status;
}
int
nfs4_do_setattr(struct nfs_server *server, struct nfs_fattr *fattr,
struct nfs4_state *nfs4_do_open(struct inode *dir, struct qstr *name, int flags, struct iattr *sattr, struct rpc_cred *cred)
{
struct nfs4_exception exception = { };
struct nfs4_state *res;
int status;
do {
status = _nfs4_do_open(dir, name, flags, sattr, cred, &res);
if (status == 0)
break;
/* NOTE: BAD_SEQID means the server and client disagree about the
* book-keeping w.r.t. state-changing operations
* (OPEN/CLOSE/LOCK/LOCKU...)
* It is actually a sign of a bug on the client or on the server.
*
* If we receive a BAD_SEQID error in the particular case of
* doing an OPEN, we assume that nfs4_increment_seqid() will
* have unhashed the old state_owner for us, and that we can
* therefore safely retry using a new one. We should still warn
* the user though...
*/
if (status == -NFS4ERR_BAD_SEQID) {
printk(KERN_WARNING "NFS: v4 server returned a bad sequence-id error!\n");
exception.retry = 1;
continue;
}
res = ERR_PTR(nfs4_handle_exception(NFS_SERVER(dir),
status, &exception));
} while (exception.retry);
return res;
}
static int _nfs4_do_setattr(struct nfs_server *server, struct nfs_fattr *fattr,
struct nfs_fh *fhandle, struct iattr *sattr,
struct nfs4_state *state)
{
......@@ -381,9 +632,7 @@ nfs4_do_setattr(struct nfs_server *server, struct nfs_fattr *fattr,
.rpc_argp = &arg,
.rpc_resp = &res,
};
int status;
retry:
fattr->valid = 0;
if (sattr->ia_valid & ATTR_SIZE)
......@@ -391,13 +640,22 @@ nfs4_do_setattr(struct nfs_server *server, struct nfs_fattr *fattr,
else
memcpy(&arg.stateid, &zero_stateid, sizeof(arg.stateid));
status = rpc_call_sync(server->client, &msg, 0);
if (status) {
status = nfs4_handle_error(server, status);
if (!status)
goto retry;
}
return status;
return rpc_call_sync(server->client, &msg, 0);
}
int nfs4_do_setattr(struct nfs_server *server, struct nfs_fattr *fattr,
struct nfs_fh *fhandle, struct iattr *sattr,
struct nfs4_state *state)
{
struct nfs4_exception exception = { };
int err;
do {
err = nfs4_handle_exception(server,
_nfs4_do_setattr(server, fattr, fhandle, sattr,
state),
&exception);
} while (exception.retry);
return err;
}
/*
......@@ -411,8 +669,7 @@ nfs4_do_setattr(struct nfs_server *server, struct nfs_fattr *fattr,
*
* NOTE: Caller must be holding the sp->so_owner semaphore!
*/
int
nfs4_do_close(struct inode *inode, struct nfs4_state *state)
static int _nfs4_do_close(struct inode *inode, struct nfs4_state *state)
{
struct nfs4_state_owner *sp = state->owner;
int status = 0;
......@@ -426,10 +683,12 @@ nfs4_do_close(struct inode *inode, struct nfs4_state *state)
.rpc_resp = &res,
};
if (test_bit(NFS_DELEGATED_STATE, &state->flags))
return 0;
memcpy(&arg.stateid, &state->stateid, sizeof(arg.stateid));
/* Serialization for the sequence id */
arg.seqid = sp->so_seqid,
status = rpc_call_sync(NFS_SERVER(inode)->client, &msg, 0);
status = rpc_call_sync(NFS_SERVER(inode)->client, &msg, RPC_TASK_NOINTR);
/* hmm. we are done with the inode, and in the process of freeing
* the state_owner. we keep this around to process errors
......@@ -441,15 +700,34 @@ nfs4_do_close(struct inode *inode, struct nfs4_state *state)
return status;
}
int
nfs4_do_downgrade(struct inode *inode, struct nfs4_state *state, mode_t mode)
int nfs4_do_close(struct inode *inode, struct nfs4_state *state)
{
struct nfs_server *server = NFS_SERVER(state->inode);
struct nfs4_exception exception = { };
int err;
do {
err = _nfs4_do_close(inode, state);
switch (err) {
case -NFS4ERR_STALE_STATEID:
case -NFS4ERR_EXPIRED:
nfs4_schedule_state_recovery(server->nfs4_state);
err = 0;
default:
state->state = 0;
}
err = nfs4_handle_exception(server, err, &exception);
} while (exception.retry);
return err;
}
static int _nfs4_do_downgrade(struct inode *inode, struct nfs4_state *state, mode_t mode)
{
struct nfs4_state_owner *sp = state->owner;
int status = 0;
struct nfs_closeargs arg = {
.fh = NFS_FH(inode),
.seqid = sp->so_seqid,
.share_access = mode,
.open_flags = mode,
};
struct nfs_closeres res;
struct rpc_message msg = {
......@@ -458,8 +736,10 @@ nfs4_do_downgrade(struct inode *inode, struct nfs4_state *state, mode_t mode)
.rpc_resp = &res,
};
if (test_bit(NFS_DELEGATED_STATE, &state->flags))
return 0;
memcpy(&arg.stateid, &state->stateid, sizeof(arg.stateid));
status = rpc_call_sync(NFS_SERVER(inode)->client, &msg, 0);
status = rpc_call_sync(NFS_SERVER(inode)->client, &msg, RPC_TASK_NOINTR);
nfs4_increment_seqid(status, sp);
if (!status)
memcpy(&state->stateid, &res.stateid, sizeof(state->stateid));
......@@ -467,6 +747,26 @@ nfs4_do_downgrade(struct inode *inode, struct nfs4_state *state, mode_t mode)
return status;
}
int nfs4_do_downgrade(struct inode *inode, struct nfs4_state *state, mode_t mode)
{
struct nfs_server *server = NFS_SERVER(state->inode);
struct nfs4_exception exception = { };
int err;
do {
err = _nfs4_do_downgrade(inode, state, mode);
switch (err) {
case -NFS4ERR_STALE_STATEID:
case -NFS4ERR_EXPIRED:
nfs4_schedule_state_recovery(server->nfs4_state);
err = 0;
default:
state->state = mode;
}
err = nfs4_handle_exception(server, err, &exception);
} while (exception.retry);
return err;
}
struct inode *
nfs4_atomic_open(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
{
......@@ -500,7 +800,9 @@ nfs4_open_revalidate(struct inode *dir, struct dentry *dentry, int openflags)
struct inode *inode;
cred = rpcauth_lookupcred(NFS_SERVER(dir)->client->cl_auth, 0);
state = nfs4_do_open(dir, &dentry->d_name, openflags, NULL, cred);
state = nfs4_open_delegated(dentry->d_inode, openflags, cred);
if (IS_ERR(state))
state = nfs4_do_open(dir, &dentry->d_name, openflags, NULL, cred);
put_rpccred(cred);
if (state == ERR_PTR(-ENOENT) && dentry->d_inode == 0)
return 1;
......@@ -518,7 +820,7 @@ nfs4_open_revalidate(struct inode *dir, struct dentry *dentry, int openflags)
}
static int nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle)
static int _nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle)
{
struct nfs4_server_caps_res res = {};
struct rpc_message msg = {
......@@ -542,7 +844,19 @@ static int nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fh
return status;
}
static int nfs4_lookup_root(struct nfs_server *server, struct nfs_fh *fhandle,
static int nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle)
{
struct nfs4_exception exception = { };
int err;
do {
err = nfs4_handle_exception(server,
_nfs4_server_capabilities(server, fhandle),
&exception);
} while (exception.retry);
return err;
}
static int _nfs4_lookup_root(struct nfs_server *server, struct nfs_fh *fhandle,
struct nfs_fsinfo *info)
{
struct nfs_fattr * fattr = info->fattr;
......@@ -563,6 +877,19 @@ static int nfs4_lookup_root(struct nfs_server *server, struct nfs_fh *fhandle,
return rpc_call_sync(server->client, &msg, 0);
}
static int nfs4_lookup_root(struct nfs_server *server, struct nfs_fh *fhandle,
struct nfs_fsinfo *info)
{
struct nfs4_exception exception = { };
int err;
do {
err = nfs4_handle_exception(server,
_nfs4_lookup_root(server, fhandle, info),
&exception);
} while (exception.retry);
return err;
}
static int nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
struct nfs_fsinfo *info)
{
......@@ -597,6 +924,8 @@ static int nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
p = server->mnt_path;
for (;;) {
struct nfs4_exception exception = { };
while (*p == '/')
p++;
if (!*p)
......@@ -606,9 +935,13 @@ static int nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
p++;
q.len = p - q.name;
fattr->valid = 0;
status = rpc_call_sync(server->client, &msg, 0);
if (!status)
do {
fattr->valid = 0;
status = nfs4_handle_exception(server,
rpc_call_sync(server->client, &msg, 0),
&exception);
} while (exception.retry);
if (status == 0)
continue;
if (status == -ENOENT) {
printk(KERN_NOTICE "NFS: mount path %s does not exist!\n", server->mnt_path);
......@@ -621,14 +954,13 @@ static int nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
if (status == 0)
status = nfs4_do_fsinfo(server, fhandle, info);
out:
return nfs4_map_errors(status);
return status;
}
static int nfs4_proc_getattr(struct inode *inode, struct nfs_fattr *fattr)
static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr)
{
struct nfs_server *server = NFS_SERVER(inode);
struct nfs4_getattr_arg args = {
.fh = NFS_FH(inode),
.fh = fhandle,
.bitmask = server->attr_bitmask,
};
struct nfs4_getattr_res res = {
......@@ -642,8 +974,19 @@ static int nfs4_proc_getattr(struct inode *inode, struct nfs_fattr *fattr)
};
fattr->valid = 0;
return rpc_call_sync(server->client, &msg, 0);
}
return nfs4_map_errors(rpc_call_sync(NFS_CLIENT(inode), &msg, 0));
static int nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr)
{
struct nfs4_exception exception = { };
int err;
do {
err = nfs4_handle_exception(server,
_nfs4_proc_getattr(server, fhandle, fattr),
&exception);
} while (exception.retry);
return err;
}
/*
......@@ -678,9 +1021,13 @@ nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
if (size_change) {
struct rpc_cred *cred = rpcauth_lookupcred(NFS_SERVER(inode)->client->cl_auth, 0);
state = nfs4_find_state(inode, cred, FMODE_WRITE);
if (!state) {
state = nfs4_do_open(dentry->d_parent->d_inode,
&dentry->d_name, FMODE_WRITE, NULL, cred);
if (state == NULL) {
state = nfs4_open_delegated(dentry->d_inode,
FMODE_WRITE, cred);
if (IS_ERR(state))
state = nfs4_do_open(dentry->d_parent->d_inode,
&dentry->d_name, FMODE_WRITE,
NULL, cred);
need_iput = 1;
}
put_rpccred(cred);
......@@ -705,7 +1052,7 @@ nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
return status;
}
static int nfs4_proc_lookup(struct inode *dir, struct qstr *name,
static int _nfs4_proc_lookup(struct inode *dir, struct qstr *name,
struct nfs_fh *fhandle, struct nfs_fattr *fattr)
{
int status;
......@@ -731,12 +1078,23 @@ static int nfs4_proc_lookup(struct inode *dir, struct qstr *name,
dprintk("NFS call lookup %s\n", name->name);
status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
dprintk("NFS reply lookup: %d\n", status);
return nfs4_map_errors(status);
return status;
}
static int nfs4_proc_access(struct inode *inode, struct rpc_cred *cred, int mode)
static int nfs4_proc_lookup(struct inode *dir, struct qstr *name, struct nfs_fh *fhandle, struct nfs_fattr *fattr)
{
struct nfs4_exception exception = { };
int err;
do {
err = nfs4_handle_exception(NFS_SERVER(dir),
_nfs4_proc_lookup(dir, name, fhandle, fattr),
&exception);
} while (exception.retry);
return err;
}
static int _nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry)
{
int status;
struct nfs4_accessargs args = {
.fh = NFS_FH(inode),
};
......@@ -745,8 +1103,10 @@ static int nfs4_proc_access(struct inode *inode, struct rpc_cred *cred, int mode
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_ACCESS],
.rpc_argp = &args,
.rpc_resp = &res,
.rpc_cred = cred,
.rpc_cred = entry->cred,
};
int mode = entry->mask;
int status;
/*
* Determine which access bits we want to ask for...
......@@ -758,8 +1118,7 @@ static int nfs4_proc_access(struct inode *inode, struct rpc_cred *cred, int mode
args.access |= NFS4_ACCESS_MODIFY | NFS4_ACCESS_EXTEND | NFS4_ACCESS_DELETE;
if (mode & MAY_EXEC)
args.access |= NFS4_ACCESS_LOOKUP;
}
else {
} else {
if (mode & MAY_WRITE)
args.access |= NFS4_ACCESS_MODIFY | NFS4_ACCESS_EXTEND;
if (mode & MAY_EXEC)
......@@ -767,13 +1126,27 @@ static int nfs4_proc_access(struct inode *inode, struct rpc_cred *cred, int mode
}
status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
if (!status) {
if (args.access != res.supported) {
printk(KERN_NOTICE "NFS: server didn't support all access bits!\n");
status = -ENOTSUPP;
} else if ((args.access & res.access) != args.access)
status = -EACCES;
entry->mask = 0;
if (res.access & NFS4_ACCESS_READ)
entry->mask |= MAY_READ;
if (res.access & (NFS4_ACCESS_MODIFY | NFS4_ACCESS_EXTEND | NFS4_ACCESS_DELETE))
entry->mask |= MAY_WRITE;
if (res.access & (NFS4_ACCESS_LOOKUP|NFS4_ACCESS_EXECUTE))
entry->mask |= MAY_EXEC;
}
return nfs4_map_errors(status);
return status;
}
static int nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry)
{
struct nfs4_exception exception = { };
int err;
do {
err = nfs4_handle_exception(NFS_SERVER(inode),
_nfs4_proc_access(inode, entry),
&exception);
} while (exception.retry);
return err;
}
/*
......@@ -800,7 +1173,7 @@ static int nfs4_proc_access(struct inode *inode, struct rpc_cred *cred, int mode
* Both of these changes to the XDR layer would in fact be quite
* minor, but I decided to leave them for a subsequent patch.
*/
static int nfs4_proc_readlink(struct inode *inode, struct page *page)
static int _nfs4_proc_readlink(struct inode *inode, struct page *page)
{
struct nfs4_readlink args = {
.fh = NFS_FH(inode),
......@@ -813,11 +1186,22 @@ static int nfs4_proc_readlink(struct inode *inode, struct page *page)
.rpc_resp = NULL,
};
return nfs4_map_errors(rpc_call_sync(NFS_CLIENT(inode), &msg, 0));
return rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
}
static int
nfs4_proc_read(struct nfs_read_data *rdata, struct file *filp)
static int nfs4_proc_readlink(struct inode *inode, struct page *page)
{
struct nfs4_exception exception = { };
int err;
do {
err = nfs4_handle_exception(NFS_SERVER(inode),
_nfs4_proc_readlink(inode, page),
&exception);
} while (exception.retry);
return err;
}
static int _nfs4_proc_read(struct nfs_read_data *rdata)
{
int flags = rdata->flags;
struct inode *inode = rdata->inode;
......@@ -827,6 +1211,7 @@ nfs4_proc_read(struct nfs_read_data *rdata, struct file *filp)
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_READ],
.rpc_argp = &rdata->args,
.rpc_resp = &rdata->res,
.rpc_cred = rdata->cred,
};
unsigned long timestamp = jiffies;
int status;
......@@ -834,29 +1219,27 @@ nfs4_proc_read(struct nfs_read_data *rdata, struct file *filp)
dprintk("NFS call read %d @ %Ld\n", rdata->args.count,
(long long) rdata->args.offset);
/*
* Try first to use O_RDONLY, then O_RDWR stateid.
*/
if (filp) {
struct nfs4_state *state;
state = (struct nfs4_state *)filp->private_data;
rdata->args.state = state;
msg.rpc_cred = state->owner->so_cred;
} else {
rdata->args.state = NULL;
msg.rpc_cred = NFS_I(inode)->mm_cred;
}
fattr->valid = 0;
status = rpc_call_sync(server->client, &msg, flags);
if (!status)
renew_lease(server, timestamp);
dprintk("NFS reply read: %d\n", status);
return nfs4_map_errors(status);
return status;
}
static int
nfs4_proc_write(struct nfs_write_data *wdata, struct file *filp)
static int nfs4_proc_read(struct nfs_read_data *rdata)
{
struct nfs4_exception exception = { };
int err;
do {
err = nfs4_handle_exception(NFS_SERVER(rdata->inode),
_nfs4_proc_read(rdata),
&exception);
} while (exception.retry);
return err;
}
static int _nfs4_proc_write(struct nfs_write_data *wdata)
{
int rpcflags = wdata->flags;
struct inode *inode = wdata->inode;
......@@ -866,33 +1249,32 @@ nfs4_proc_write(struct nfs_write_data *wdata, struct file *filp)
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_WRITE],
.rpc_argp = &wdata->args,
.rpc_resp = &wdata->res,
.rpc_cred = wdata->cred,
};
int status;
dprintk("NFS call write %d @ %Ld\n", wdata->args.count,
(long long) wdata->args.offset);
/*
* Try first to use O_WRONLY, then O_RDWR stateid.
*/
if (filp) {
struct nfs4_state *state;
state = (struct nfs4_state *)filp->private_data;
wdata->args.state = state;
msg.rpc_cred = state->owner->so_cred;
} else {
wdata->args.state = NULL;
msg.rpc_cred = NFS_I(inode)->mm_cred;
}
fattr->valid = 0;
status = rpc_call_sync(server->client, &msg, rpcflags);
dprintk("NFS reply write: %d\n", status);
return nfs4_map_errors(status);
return status;
}
static int
nfs4_proc_commit(struct nfs_write_data *cdata, struct file *filp)
static int nfs4_proc_write(struct nfs_write_data *wdata)
{
struct nfs4_exception exception = { };
int err;
do {
err = nfs4_handle_exception(NFS_SERVER(wdata->inode),
_nfs4_proc_write(wdata),
&exception);
} while (exception.retry);
return err;
}
static int _nfs4_proc_commit(struct nfs_write_data *cdata)
{
struct inode *inode = cdata->inode;
struct nfs_fattr *fattr = cdata->res.fattr;
......@@ -901,24 +1283,29 @@ nfs4_proc_commit(struct nfs_write_data *cdata, struct file *filp)
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_COMMIT],
.rpc_argp = &cdata->args,
.rpc_resp = &cdata->res,
.rpc_cred = cdata->cred,
};
int status;
dprintk("NFS call commit %d @ %Ld\n", cdata->args.count,
(long long) cdata->args.offset);
/*
* Try first to use O_WRONLY, then O_RDWR stateid.
*/
if (filp)
msg.rpc_cred = ((struct nfs4_state *)filp->private_data)->owner->so_cred;
else
msg.rpc_cred = NFS_I(inode)->mm_cred;
fattr->valid = 0;
status = rpc_call_sync(server->client, &msg, 0);
dprintk("NFS reply commit: %d\n", status);
return nfs4_map_errors(status);
return status;
}
static int nfs4_proc_commit(struct nfs_write_data *cdata)
{
struct nfs4_exception exception = { };
int err;
do {
err = nfs4_handle_exception(NFS_SERVER(cdata->inode),
_nfs4_proc_commit(cdata),
&exception);
} while (exception.retry);
return err;
}
/*
......@@ -965,7 +1352,7 @@ nfs4_proc_create(struct inode *dir, struct qstr *name, struct iattr *sattr,
return inode;
}
static int nfs4_proc_remove(struct inode *dir, struct qstr *name)
static int _nfs4_proc_remove(struct inode *dir, struct qstr *name)
{
struct nfs4_remove_arg args = {
.fh = NFS_FH(dir),
......@@ -982,7 +1369,19 @@ static int nfs4_proc_remove(struct inode *dir, struct qstr *name)
status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
if (status == 0)
update_changeattr(dir, &res);
return nfs4_map_errors(status);
return status;
}
static int nfs4_proc_remove(struct inode *dir, struct qstr *name)
{
struct nfs4_exception exception = { };
int err;
do {
err = nfs4_handle_exception(NFS_SERVER(dir),
_nfs4_proc_remove(dir, name),
&exception);
} while (exception.retry);
return err;
}
struct unlink_desc {
......@@ -1023,7 +1422,7 @@ static int nfs4_proc_unlink_done(struct dentry *dir, struct rpc_task *task)
return 0;
}
static int nfs4_proc_rename(struct inode *old_dir, struct qstr *old_name,
static int _nfs4_proc_rename(struct inode *old_dir, struct qstr *old_name,
struct inode *new_dir, struct qstr *new_name)
{
struct nfs4_rename_arg arg = {
......@@ -1046,10 +1445,24 @@ static int nfs4_proc_rename(struct inode *old_dir, struct qstr *old_name,
update_changeattr(old_dir, &res.old_cinfo);
update_changeattr(new_dir, &res.new_cinfo);
}
return nfs4_map_errors(status);
return status;
}
static int nfs4_proc_link(struct inode *inode, struct inode *dir, struct qstr *name)
static int nfs4_proc_rename(struct inode *old_dir, struct qstr *old_name,
struct inode *new_dir, struct qstr *new_name)
{
struct nfs4_exception exception = { };
int err;
do {
err = nfs4_handle_exception(NFS_SERVER(old_dir),
_nfs4_proc_rename(old_dir, old_name,
new_dir, new_name),
&exception);
} while (exception.retry);
return err;
}
static int _nfs4_proc_link(struct inode *inode, struct inode *dir, struct qstr *name)
{
struct nfs4_link_arg arg = {
.fh = NFS_FH(inode),
......@@ -1068,10 +1481,22 @@ static int nfs4_proc_link(struct inode *inode, struct inode *dir, struct qstr *n
if (!status)
update_changeattr(dir, &cinfo);
return nfs4_map_errors(status);
return status;
}
static int nfs4_proc_symlink(struct inode *dir, struct qstr *name,
static int nfs4_proc_link(struct inode *inode, struct inode *dir, struct qstr *name)
{
struct nfs4_exception exception = { };
int err;
do {
err = nfs4_handle_exception(NFS_SERVER(inode),
_nfs4_proc_link(inode, dir, name),
&exception);
} while (exception.retry);
return err;
}
static int _nfs4_proc_symlink(struct inode *dir, struct qstr *name,
struct qstr *path, struct iattr *sattr, struct nfs_fh *fhandle,
struct nfs_fattr *fattr)
{
......@@ -1090,22 +1515,39 @@ static int nfs4_proc_symlink(struct inode *dir, struct qstr *name,
.fattr = fattr,
};
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CREATE],
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SYMLINK],
.rpc_argp = &arg,
.rpc_resp = &res,
};
int status;
if (path->len > NFS4_MAXPATHLEN)
return -ENAMETOOLONG;
arg.u.symlink = path;
fattr->valid = 0;
status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
if (!status)
update_changeattr(dir, &res.dir_cinfo);
return nfs4_map_errors(status);
return status;
}
static int nfs4_proc_mkdir(struct inode *dir, struct qstr *name,
static int nfs4_proc_symlink(struct inode *dir, struct qstr *name,
struct qstr *path, struct iattr *sattr, struct nfs_fh *fhandle,
struct nfs_fattr *fattr)
{
struct nfs4_exception exception = { };
int err;
do {
err = nfs4_handle_exception(NFS_SERVER(dir),
_nfs4_proc_symlink(dir, name, path, sattr,
fhandle, fattr),
&exception);
} while (exception.retry);
return err;
}
static int _nfs4_proc_mkdir(struct inode *dir, struct qstr *name,
struct iattr *sattr, struct nfs_fh *fhandle,
struct nfs_fattr *fattr)
{
......@@ -1135,10 +1577,25 @@ static int nfs4_proc_mkdir(struct inode *dir, struct qstr *name,
status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
if (!status)
update_changeattr(dir, &res.dir_cinfo);
return nfs4_map_errors(status);
return status;
}
static int nfs4_proc_readdir(struct dentry *dentry, struct rpc_cred *cred,
static int nfs4_proc_mkdir(struct inode *dir, struct qstr *name,
struct iattr *sattr, struct nfs_fh *fhandle,
struct nfs_fattr *fattr)
{
struct nfs4_exception exception = { };
int err;
do {
err = nfs4_handle_exception(NFS_SERVER(dir),
_nfs4_proc_mkdir(dir, name, sattr,
fhandle, fattr),
&exception);
} while (exception.retry);
return err;
}
static int _nfs4_proc_readdir(struct dentry *dentry, struct rpc_cred *cred,
u64 cookie, struct page *page, unsigned int count, int plus)
{
struct inode *dir = dentry->d_inode;
......@@ -1164,10 +1621,24 @@ static int nfs4_proc_readdir(struct dentry *dentry, struct rpc_cred *cred,
if (status == 0)
memcpy(NFS_COOKIEVERF(dir), res.verifier.data, NFS4_VERIFIER_SIZE);
unlock_kernel();
return nfs4_map_errors(status);
return status;
}
static int nfs4_proc_mknod(struct inode *dir, struct qstr *name,
static int nfs4_proc_readdir(struct dentry *dentry, struct rpc_cred *cred,
u64 cookie, struct page *page, unsigned int count, int plus)
{
struct nfs4_exception exception = { };
int err;
do {
err = nfs4_handle_exception(NFS_SERVER(dentry->d_inode),
_nfs4_proc_readdir(dentry, cred, cookie,
page, count, plus),
&exception);
} while (exception.retry);
return err;
}
static int _nfs4_proc_mknod(struct inode *dir, struct qstr *name,
struct iattr *sattr, dev_t rdev, struct nfs_fh *fh,
struct nfs_fattr *fattr)
{
......@@ -1214,10 +1685,25 @@ static int nfs4_proc_mknod(struct inode *dir, struct qstr *name,
status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
if (!status)
update_changeattr(dir, &res.dir_cinfo);
return nfs4_map_errors(status);
return status;
}
static int nfs4_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle,
static int nfs4_proc_mknod(struct inode *dir, struct qstr *name,
struct iattr *sattr, dev_t rdev, struct nfs_fh *fh,
struct nfs_fattr *fattr)
{
struct nfs4_exception exception = { };
int err;
do {
err = nfs4_handle_exception(NFS_SERVER(dir),
_nfs4_proc_mknod(dir, name, sattr, rdev,
fh, fattr),
&exception);
} while (exception.retry);
return err;
}
static int _nfs4_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle,
struct nfs_fsstat *fsstat)
{
struct nfs4_statfs_arg args = {
......@@ -1231,10 +1717,22 @@ static int nfs4_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle,
};
fsstat->fattr->valid = 0;
return nfs4_map_errors(rpc_call_sync(server->client, &msg, 0));
return rpc_call_sync(server->client, &msg, 0);
}
static int nfs4_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fsstat *fsstat)
{
struct nfs4_exception exception = { };
int err;
do {
err = nfs4_handle_exception(server,
_nfs4_proc_statfs(server, fhandle, fsstat),
&exception);
} while (exception.retry);
return err;
}
static int nfs4_do_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle,
static int _nfs4_do_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle,
struct nfs_fsinfo *fsinfo)
{
struct nfs4_fsinfo_arg args = {
......@@ -1247,16 +1745,29 @@ static int nfs4_do_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle,
.rpc_resp = fsinfo,
};
return nfs4_map_errors(rpc_call_sync(server->client, &msg, 0));
return rpc_call_sync(server->client, &msg, 0);
}
static int nfs4_do_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fsinfo *fsinfo)
{
struct nfs4_exception exception = { };
int err;
do {
err = nfs4_handle_exception(server,
_nfs4_do_fsinfo(server, fhandle, fsinfo),
&exception);
} while (exception.retry);
return err;
}
static int nfs4_proc_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fsinfo *fsinfo)
{
fsinfo->fattr->valid = 0;
return nfs4_map_errors(nfs4_do_fsinfo(server, fhandle, fsinfo));
return nfs4_do_fsinfo(server, fhandle, fsinfo);
}
static int nfs4_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle,
static int _nfs4_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle,
struct nfs_pathconf *pathconf)
{
struct nfs4_pathconf_arg args = {
......@@ -1276,7 +1787,21 @@ static int nfs4_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle,
}
pathconf->fattr->valid = 0;
return nfs4_map_errors(rpc_call_sync(server->client, &msg, 0));
return rpc_call_sync(server->client, &msg, 0);
}
static int nfs4_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle,
struct nfs_pathconf *pathconf)
{
struct nfs4_exception exception = { };
int err;
do {
err = nfs4_handle_exception(server,
_nfs4_proc_pathconf(server, fhandle, pathconf),
&exception);
} while (exception.retry);
return err;
}
static void
......@@ -1415,9 +1940,11 @@ renew_done(struct rpc_task *task)
if (task->tk_status < 0) {
switch (task->tk_status) {
case -NFS4ERR_STALE_CLIENTID:
case -NFS4ERR_EXPIRED:
case -NFS4ERR_CB_PATH_DOWN:
nfs4_schedule_state_recovery(clp);
return;
}
return;
}
spin_lock(&clp->cl_lock);
if (time_before(clp->cl_last_renewal,timestamp))
......@@ -1450,11 +1977,13 @@ nfs4_proc_renew(struct nfs4_client *clp)
int status;
status = rpc_call_sync(clp->cl_rpcclient, &msg, 0);
if (status < 0)
return status;
spin_lock(&clp->cl_lock);
if (time_before(clp->cl_last_renewal,now))
clp->cl_last_renewal = now;
spin_unlock(&clp->cl_lock);
return status;
return 0;
}
/*
......@@ -1467,8 +1996,10 @@ static int
nfs4_proc_file_open(struct inode *inode, struct file *filp)
{
struct dentry *dentry = filp->f_dentry;
struct nfs4_state *state;
struct nfs_open_context *ctx;
struct nfs4_state *state = NULL;
struct rpc_cred *cred;
int status = -ENOMEM;
dprintk("nfs4_proc_file_open: starting on (%.*s/%.*s)\n",
(int)dentry->d_parent->d_name.len,
......@@ -1478,21 +2009,28 @@ nfs4_proc_file_open(struct inode *inode, struct file *filp)
/* Find our open stateid */
cred = rpcauth_lookupcred(NFS_SERVER(inode)->client->cl_auth, 0);
state = nfs4_find_state(inode, cred, filp->f_mode);
if (unlikely(cred == NULL))
return -ENOMEM;
ctx = alloc_nfs_open_context(dentry, cred);
put_rpccred(cred);
if (state == NULL) {
printk(KERN_WARNING "NFS: v4 raced in function %s\n", __FUNCTION__);
return -EIO; /* ERACE actually */
}
if (unlikely(ctx == NULL))
return -ENOMEM;
status = -EIO; /* ERACE actually */
state = nfs4_find_state(inode, cred, filp->f_mode);
if (unlikely(state == NULL))
goto no_state;
ctx->state = state;
nfs4_close_state(state, filp->f_mode);
if (filp->f_mode & FMODE_WRITE) {
lock_kernel();
nfs_set_mmcred(inode, state->owner->so_cred);
ctx->mode = filp->f_mode;
nfs_file_set_open_context(filp, ctx);
put_nfs_open_context(ctx);
if (filp->f_mode & FMODE_WRITE)
nfs_begin_data_update(inode);
unlock_kernel();
}
filp->private_data = state;
return 0;
no_state:
printk(KERN_WARNING "NFS: v4 raced in function %s\n", __FUNCTION__);
put_nfs_open_context(ctx);
return status;
}
/*
......@@ -1501,37 +2039,12 @@ nfs4_proc_file_open(struct inode *inode, struct file *filp)
static int
nfs4_proc_file_release(struct inode *inode, struct file *filp)
{
struct nfs4_state *state = (struct nfs4_state *)filp->private_data;
if (state)
nfs4_close_state(state, filp->f_mode);
if (filp->f_mode & FMODE_WRITE) {
lock_kernel();
if (filp->f_mode & FMODE_WRITE)
nfs_end_data_update(inode);
unlock_kernel();
}
nfs_file_clear_open_context(filp);
return 0;
}
/*
* Set up the nfspage struct with the right state info and credentials
*/
static void
nfs4_request_init(struct nfs_page *req, struct file *filp)
{
struct nfs4_state *state;
if (!filp) {
req->wb_cred = get_rpccred(NFS_I(req->wb_inode)->mm_cred);
req->wb_state = NULL;
return;
}
state = (struct nfs4_state *)filp->private_data;
req->wb_state = state;
req->wb_cred = get_rpccred(state->owner->so_cred);
req->wb_lockowner = current->files;
}
static int
nfs4_async_handle_error(struct rpc_task *task, struct nfs_server *server)
{
......@@ -1545,11 +2058,13 @@ nfs4_async_handle_error(struct rpc_task *task, struct nfs_server *server)
case -NFS4ERR_EXPIRED:
rpc_sleep_on(&clp->cl_rpcwaitq, task, NULL, NULL);
nfs4_schedule_state_recovery(clp);
if (test_bit(NFS4CLNT_OK, &clp->cl_state))
rpc_wake_up_task(task);
task->tk_status = 0;
return -EAGAIN;
case -NFS4ERR_GRACE:
case -NFS4ERR_DELAY:
rpc_delay(task, NFS4_POLL_RETRY_TIME);
rpc_delay(task, NFS4_POLL_RETRY_MAX);
task->tk_status = 0;
return -EAGAIN;
case -NFS4ERR_OLD_STATEID:
......@@ -1560,12 +2075,11 @@ nfs4_async_handle_error(struct rpc_task *task, struct nfs_server *server)
return 0;
}
int
nfs4_wait_clnt_recover(struct rpc_clnt *clnt, struct nfs4_client *clp)
int nfs4_wait_clnt_recover(struct rpc_clnt *clnt, struct nfs4_client *clp)
{
DEFINE_WAIT(wait);
sigset_t oldset;
int interruptible, res;
int interruptible, res = 0;
might_sleep();
......@@ -1573,101 +2087,85 @@ nfs4_wait_clnt_recover(struct rpc_clnt *clnt, struct nfs4_client *clp)
interruptible = TASK_UNINTERRUPTIBLE;
if (clnt->cl_intr)
interruptible = TASK_INTERRUPTIBLE;
do {
res = 0;
prepare_to_wait(&clp->cl_waitq, &wait, interruptible);
nfs4_schedule_state_recovery(clp);
if (test_bit(NFS4CLNT_OK, &clp->cl_state) &&
!test_bit(NFS4CLNT_SETUP_STATE, &clp->cl_state))
break;
if (clnt->cl_intr && signalled()) {
res = -ERESTARTSYS;
break;
}
prepare_to_wait(&clp->cl_waitq, &wait, interruptible);
nfs4_schedule_state_recovery(clp);
if (clnt->cl_intr && signalled())
res = -ERESTARTSYS;
else if (!test_bit(NFS4CLNT_OK, &clp->cl_state))
schedule();
} while(!test_bit(NFS4CLNT_OK, &clp->cl_state));
finish_wait(&clp->cl_waitq, &wait);
rpc_clnt_sigunmask(clnt, &oldset);
return res;
}
static int
nfs4_delay(struct rpc_clnt *clnt)
static int nfs4_delay(struct rpc_clnt *clnt, long *timeout)
{
sigset_t oldset;
int res = 0;
might_sleep();
if (*timeout <= 0)
*timeout = NFS4_POLL_RETRY_MIN;
if (*timeout > NFS4_POLL_RETRY_MAX)
*timeout = NFS4_POLL_RETRY_MAX;
rpc_clnt_sigmask(clnt, &oldset);
if (clnt->cl_intr) {
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(NFS4_POLL_RETRY_TIME);
schedule_timeout(*timeout);
if (signalled())
res = -ERESTARTSYS;
} else {
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(NFS4_POLL_RETRY_TIME);
schedule_timeout(*timeout);
}
rpc_clnt_sigunmask(clnt, &oldset);
*timeout <<= 1;
return res;
}
/* This is the error handling routine for processes that are allowed
* to sleep.
*/
int
nfs4_handle_error(struct nfs_server *server, int errorcode)
int nfs4_handle_exception(struct nfs_server *server, int errorcode, struct nfs4_exception *exception)
{
struct nfs4_client *clp = server->nfs4_state;
int ret = errorcode;
exception->retry = 0;
switch(errorcode) {
case 0:
return 0;
case -NFS4ERR_STALE_CLIENTID:
case -NFS4ERR_STALE_STATEID:
case -NFS4ERR_EXPIRED:
ret = nfs4_wait_clnt_recover(server->client, clp);
if (ret == 0)
exception->retry = 1;
break;
case -NFS4ERR_GRACE:
case -NFS4ERR_DELAY:
ret = nfs4_delay(server->client);
ret = nfs4_delay(server->client, &exception->timeout);
if (ret == 0)
exception->retry = 1;
break;
case -NFS4ERR_OLD_STATEID:
ret = 0;
if (ret == 0)
exception->retry = 1;
}
/* We failed to handle the error */
return nfs4_map_errors(ret);
}
static int
nfs4_request_compatible(struct nfs_page *req, struct file *filp, struct page *page)
{
struct nfs4_state *state = NULL;
struct rpc_cred *cred = NULL;
if (req->wb_file != filp)
return 0;
if (req->wb_page != page)
return 0;
state = (struct nfs4_state *)filp->private_data;
if (req->wb_state != state)
return 0;
if (req->wb_lockowner != current->files)
return 0;
cred = state->owner->so_cred;
if (req->wb_cred != cred)
return 0;
return 1;
}
int
nfs4_proc_setclientid(struct nfs4_client *clp,
u32 program, unsigned short port)
int nfs4_proc_setclientid(struct nfs4_client *clp, u32 program, unsigned short port)
{
u32 *p;
struct nfs4_setclientid setclientid;
struct timespec tv;
static nfs4_verifier sc_verifier;
static int initialized;
struct nfs4_setclientid setclientid = {
.sc_verifier = &sc_verifier,
.sc_prog = program,
};
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SETCLIENTID],
.rpc_argp = &setclientid,
......@@ -1675,15 +2173,24 @@ nfs4_proc_setclientid(struct nfs4_client *clp,
.rpc_cred = clp->cl_cred,
};
tv = CURRENT_TIME;
p = (u32*)setclientid.sc_verifier.data;
*p++ = (u32)tv.tv_sec;
*p = (u32)tv.tv_nsec;
setclientid.sc_name = clp->cl_ipaddr;
sprintf(setclientid.sc_netid, "tcp");
sprintf(setclientid.sc_uaddr, "%s.%d.%d", clp->cl_ipaddr, port >> 8, port & 255);
setclientid.sc_prog = htonl(program);
setclientid.sc_cb_ident = 0;
if (!initialized) {
struct timespec boot_time;
u32 *p;
initialized = 1;
boot_time = CURRENT_TIME;
p = (u32*)sc_verifier.data;
*p++ = htonl((u32)boot_time.tv_sec);
*p = htonl((u32)boot_time.tv_nsec);
}
setclientid.sc_name_len = scnprintf(setclientid.sc_name,
sizeof(setclientid.sc_name), "%s/%u.%u.%u.%u",
clp->cl_ipaddr, NIPQUAD(clp->cl_addr.s_addr));
setclientid.sc_netid_len = scnprintf(setclientid.sc_netid,
sizeof(setclientid.sc_netid), "tcp");
setclientid.sc_uaddr_len = scnprintf(setclientid.sc_uaddr,
sizeof(setclientid.sc_uaddr), "%s.%d.%d",
clp->cl_ipaddr, port >> 8, port & 255);
return rpc_call_sync(clp->cl_rpcclient, &msg, 0);
}
......@@ -1712,6 +2219,40 @@ nfs4_proc_setclientid_confirm(struct nfs4_client *clp)
return status;
}
static int _nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, const nfs4_stateid *stateid)
{
struct nfs4_delegreturnargs args = {
.fhandle = NFS_FH(inode),
.stateid = stateid,
};
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_DELEGRETURN],
.rpc_argp = &args,
.rpc_cred = cred,
};
return rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
}
int nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, const nfs4_stateid *stateid)
{
struct nfs_server *server = NFS_SERVER(inode);
struct nfs4_exception exception = { };
int err;
do {
err = _nfs4_proc_delegreturn(inode, cred, stateid);
switch (err) {
case -NFS4ERR_STALE_STATEID:
case -NFS4ERR_EXPIRED:
nfs4_schedule_state_recovery(server->nfs4_state);
case 0:
return 0;
}
err = nfs4_handle_exception(server, err, &exception);
} while (exception.retry);
return err;
}
#define NFS4_LOCK_MINTIMEOUT (1 * HZ)
#define NFS4_LOCK_MAXTIMEOUT (30 * HZ)
......@@ -1753,8 +2294,7 @@ nfs4_lck_length(struct file_lock *request)
return request->fl_end - request->fl_start + 1;
}
int
nfs4_proc_getlk(struct nfs4_state *state, int cmd, struct file_lock *request)
static int _nfs4_proc_getlk(struct nfs4_state *state, int cmd, struct file_lock *request)
{
struct inode *inode = state->inode;
struct nfs_server *server = NFS_SERVER(inode);
......@@ -1778,6 +2318,7 @@ nfs4_proc_getlk(struct nfs4_state *state, int cmd, struct file_lock *request)
struct nfs4_lock_state *lsp;
int status;
down_read(&clp->cl_sem);
nlo.clientid = clp->cl_clientid;
down(&state->lock_sema);
lsp = nfs4_find_lock_state(state, request->fl_owner);
......@@ -1811,14 +2352,28 @@ nfs4_proc_getlk(struct nfs4_state *state, int cmd, struct file_lock *request)
if (lsp)
nfs4_put_lock_state(lsp);
up(&state->lock_sema);
return nfs4_map_errors(status);
up_read(&clp->cl_sem);
return status;
}
int
nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock *request)
static int nfs4_proc_getlk(struct nfs4_state *state, int cmd, struct file_lock *request)
{
struct nfs4_exception exception = { };
int err;
do {
err = nfs4_handle_exception(NFS_SERVER(state->inode),
_nfs4_proc_getlk(state, cmd, request),
&exception);
} while (exception.retry);
return err;
}
static int _nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock *request)
{
struct inode *inode = state->inode;
struct nfs_server *server = NFS_SERVER(inode);
struct nfs4_client *clp = server->nfs4_state;
struct nfs_lockargs arg = {
.fh = NFS_FH(inode),
.type = nfs4_lck_type(cmd, request),
......@@ -1838,31 +2393,48 @@ nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock *request)
struct nfs_locku_opargs luargs;
int status = 0;
down_read(&clp->cl_sem);
down(&state->lock_sema);
lsp = nfs4_find_lock_state(state, request->fl_owner);
if (!lsp)
goto out;
luargs.seqid = lsp->ls_seqid;
memcpy(&luargs.stateid, &lsp->ls_stateid, sizeof(luargs.stateid));
arg.u.locku = &luargs;
status = rpc_call_sync(server->client, &msg, 0);
nfs4_increment_lock_seqid(status, lsp);
/* We might have lost the locks! */
if ((lsp->ls_flags & NFS_LOCK_INITIALIZED) != 0) {
luargs.seqid = lsp->ls_seqid;
memcpy(&luargs.stateid, &lsp->ls_stateid, sizeof(luargs.stateid));
arg.u.locku = &luargs;
status = rpc_call_sync(server->client, &msg, RPC_TASK_NOINTR);
nfs4_increment_lock_seqid(status, lsp);
}
if (status == 0) {
memcpy(&lsp->ls_stateid, &res.u.stateid,
sizeof(lsp->ls_stateid));
nfs4_notify_unlck(inode, request, lsp);
nfs4_notify_unlck(state, request, lsp);
}
nfs4_put_lock_state(lsp);
out:
up(&state->lock_sema);
if (status == 0)
posix_lock_file(request->fl_file, request);
return nfs4_map_errors(status);
up_read(&clp->cl_sem);
return status;
}
static int
nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock *request)
static int nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock *request)
{
struct nfs4_exception exception = { };
int err;
do {
err = nfs4_handle_exception(NFS_SERVER(state->inode),
_nfs4_proc_unlck(state, cmd, request),
&exception);
} while (exception.retry);
return err;
}
static int _nfs4_do_setlk(struct nfs4_state *state, int cmd, struct file_lock *request, int reclaim)
{
struct inode *inode = state->inode;
struct nfs_server *server = NFS_SERVER(inode);
......@@ -1883,23 +2455,22 @@ nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock *request)
.rpc_cred = state->owner->so_cred,
};
struct nfs_lock_opargs largs = {
.reclaim = reclaim,
.new_lock_owner = 0,
};
int status;
down(&state->lock_sema);
lsp = nfs4_find_lock_state(state, request->fl_owner);
if (lsp == NULL) {
lsp = nfs4_get_lock_state(state, request->fl_owner);
if (lsp == NULL)
return -ENOMEM;
if (!(lsp->ls_flags & NFS_LOCK_INITIALIZED)) {
struct nfs4_state_owner *owner = state->owner;
struct nfs_open_to_lock otl = {
.lock_owner = {
.clientid = server->nfs4_state->cl_clientid,
},
};
status = -ENOMEM;
lsp = nfs4_alloc_lock_state(state, request->fl_owner);
if (!lsp)
goto out;
otl.lock_seqid = lsp->ls_seqid;
otl.lock_owner.id = lsp->ls_id;
memcpy(&otl.open_stateid, &state->stateid, sizeof(otl.open_stateid));
......@@ -1908,7 +2479,7 @@ nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock *request)
arg.u.lock = &largs;
down(&owner->so_sema);
otl.open_seqid = owner->so_seqid;
status = rpc_call_sync(server->client, &msg, 0);
status = rpc_call_sync(server->client, &msg, RPC_TASK_NOINTR);
/* increment open_owner seqid on success, and
* seqid mutating errors */
nfs4_increment_seqid(status, owner);
......@@ -1921,18 +2492,35 @@ nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock *request)
largs.u.exist_lock = &el;
largs.new_lock_owner = 0;
arg.u.lock = &largs;
status = rpc_call_sync(server->client, &msg, 0);
status = rpc_call_sync(server->client, &msg, RPC_TASK_NOINTR);
}
/* increment seqid on success, and * seqid mutating errors*/
nfs4_increment_lock_seqid(status, lsp);
/* save the returned stateid. */
if (status == 0) {
memcpy(&lsp->ls_stateid, &res.u.stateid, sizeof(nfs4_stateid));
nfs4_notify_setlk(inode, request, lsp);
lsp->ls_flags |= NFS_LOCK_INITIALIZED;
if (!reclaim)
nfs4_notify_setlk(state, request, lsp);
} else if (status == -NFS4ERR_DENIED)
status = -EAGAIN;
nfs4_put_lock_state(lsp);
out:
return status;
}
int nfs4_lock_reclaim(struct nfs4_state *state, struct file_lock *request)
{
return _nfs4_do_setlk(state, F_SETLK64, request, 1);
}
static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock *request)
{
struct nfs4_client *clp = state->owner->so_client;
int status;
down_read(&clp->cl_sem);
down(&state->lock_sema);
status = _nfs4_do_setlk(state, cmd, request, 0);
up(&state->lock_sema);
if (status == 0) {
/* Note: we always want to sleep here! */
......@@ -1940,19 +2528,34 @@ nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock *request)
if (posix_lock_file_wait(request->fl_file, request) < 0)
printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n", __FUNCTION__);
}
return nfs4_map_errors(status);
up_read(&clp->cl_sem);
return status;
}
static int nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock *request)
{
struct nfs4_exception exception = { };
int err;
do {
err = nfs4_handle_exception(NFS_SERVER(state->inode),
_nfs4_proc_setlk(state, cmd, request),
&exception);
} while (exception.retry);
return err;
}
static int
nfs4_proc_lock(struct file *filp, int cmd, struct file_lock *request)
{
struct nfs_open_context *ctx;
struct nfs4_state *state;
unsigned long timeout = NFS4_LOCK_MINTIMEOUT;
int status;
/* verify open state */
state = (struct nfs4_state *)filp->private_data;
BUG_ON(!state);
ctx = (struct nfs_open_context *)filp->private_data;
state = ctx->state;
if (request->fl_start < 0 || request->fl_end < 0)
return -EINVAL;
......@@ -2012,8 +2615,6 @@ struct nfs_rpc_ops nfs_v4_clientops = {
.commit_setup = nfs4_proc_commit_setup,
.file_open = nfs4_proc_file_open,
.file_release = nfs4_proc_file_release,
.request_init = nfs4_request_init,
.request_compatible = nfs4_request_compatible,
.lock = nfs4_proc_lock,
};
......
......@@ -40,11 +40,15 @@
#include <linux/config.h>
#include <linux/slab.h>
#include <linux/smp_lock.h>
#include <linux/nfs_fs.h>
#include <linux/nfs_idmap.h>
#include <linux/workqueue.h>
#include <linux/bitops.h>
#include "callback.h"
#include "delegation.h"
#define OPENOWNER_POOL_SIZE 8
static spinlock_t state_spinlock = SPIN_LOCK_UNLOCKED;
......@@ -93,21 +97,26 @@ nfs4_alloc_client(struct in_addr *addr)
{
struct nfs4_client *clp;
if ((clp = kmalloc(sizeof(*clp), GFP_KERNEL))) {
memset(clp, 0, sizeof(*clp));
memcpy(&clp->cl_addr, addr, sizeof(clp->cl_addr));
init_rwsem(&clp->cl_sem);
INIT_LIST_HEAD(&clp->cl_state_owners);
INIT_LIST_HEAD(&clp->cl_unused);
spin_lock_init(&clp->cl_lock);
atomic_set(&clp->cl_count, 1);
INIT_WORK(&clp->cl_recoverd, nfs4_recover_state, clp);
INIT_WORK(&clp->cl_renewd, nfs4_renew_state, clp);
INIT_LIST_HEAD(&clp->cl_superblocks);
init_waitqueue_head(&clp->cl_waitq);
rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS4 client");
clp->cl_state = 1 << NFS4CLNT_NEW;
if (nfs_callback_up() < 0)
return NULL;
if ((clp = kmalloc(sizeof(*clp), GFP_KERNEL)) == NULL) {
nfs_callback_down();
return NULL;
}
memset(clp, 0, sizeof(*clp));
memcpy(&clp->cl_addr, addr, sizeof(clp->cl_addr));
init_rwsem(&clp->cl_sem);
INIT_LIST_HEAD(&clp->cl_delegations);
INIT_LIST_HEAD(&clp->cl_state_owners);
INIT_LIST_HEAD(&clp->cl_unused);
spin_lock_init(&clp->cl_lock);
atomic_set(&clp->cl_count, 1);
INIT_WORK(&clp->cl_recoverd, nfs4_recover_state, clp);
INIT_WORK(&clp->cl_renewd, nfs4_renew_state, clp);
INIT_LIST_HEAD(&clp->cl_superblocks);
init_waitqueue_head(&clp->cl_waitq);
rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS4 client");
clp->cl_state = 1 << NFS4CLNT_OK;
return clp;
}
......@@ -130,25 +139,52 @@ nfs4_free_client(struct nfs4_client *clp)
if (clp->cl_rpcclient)
rpc_shutdown_client(clp->cl_rpcclient);
kfree(clp);
nfs_callback_down();
}
static struct nfs4_client *__nfs4_find_client(struct in_addr *addr)
{
struct nfs4_client *clp;
list_for_each_entry(clp, &nfs4_clientid_list, cl_servers) {
if (memcmp(&clp->cl_addr, addr, sizeof(clp->cl_addr)) == 0) {
atomic_inc(&clp->cl_count);
return clp;
}
}
return NULL;
}
struct nfs4_client *nfs4_find_client(struct in_addr *addr)
{
struct nfs4_client *clp;
spin_lock(&state_spinlock);
clp = __nfs4_find_client(addr);
spin_unlock(&state_spinlock);
return clp;
}
struct nfs4_client *
nfs4_get_client(struct in_addr *addr)
{
struct nfs4_client *new, *clp = NULL;
struct nfs4_client *clp, *new = NULL;
new = nfs4_alloc_client(addr);
spin_lock(&state_spinlock);
list_for_each_entry(clp, &nfs4_clientid_list, cl_servers) {
if (memcmp(&clp->cl_addr, addr, sizeof(clp->cl_addr)) == 0)
goto found;
for (;;) {
clp = __nfs4_find_client(addr);
if (clp != NULL)
break;
clp = new;
if (clp != NULL) {
list_add(&clp->cl_servers, &nfs4_clientid_list);
new = NULL;
break;
}
spin_unlock(&state_spinlock);
new = nfs4_alloc_client(addr);
spin_lock(&state_spinlock);
if (new == NULL)
break;
}
if (new)
list_add(&new->cl_servers, &nfs4_clientid_list);
spin_unlock(&state_spinlock);
return new;
found:
atomic_inc(&clp->cl_count);
spin_unlock(&state_spinlock);
if (new)
nfs4_free_client(new);
......@@ -169,6 +205,16 @@ nfs4_put_client(struct nfs4_client *clp)
nfs4_free_client(clp);
}
int nfs4_init_client(struct nfs4_client *clp)
{
int status = nfs4_proc_setclientid(clp, NFS4_CALLBACK, nfs_callback_tcpport);
if (status == 0)
status = nfs4_proc_setclientid_confirm(clp);
if (status == 0)
nfs4_schedule_state_renewal(clp);
return status;
}
u32
nfs4_alloc_lockowner_id(struct nfs4_client *clp)
{
......@@ -185,7 +231,6 @@ nfs4_client_grab_unused(struct nfs4_client *clp, struct rpc_cred *cred)
atomic_inc(&sp->so_count);
sp->so_cred = cred;
list_move(&sp->so_list, &clp->cl_state_owners);
sp->so_generation = clp->cl_generation;
clp->cl_nunused--;
}
return sp;
......@@ -224,6 +269,7 @@ nfs4_alloc_state_owner(void)
init_MUTEX(&sp->so_sema);
sp->so_seqid = 0; /* arbitrary */
INIT_LIST_HEAD(&sp->so_states);
INIT_LIST_HEAD(&sp->so_delegations);
atomic_set(&sp->so_count, 1);
return sp;
}
......@@ -237,8 +283,11 @@ nfs4_unhash_state_owner(struct nfs4_state_owner *sp)
spin_unlock(&clp->cl_lock);
}
struct nfs4_state_owner *
nfs4_get_state_owner(struct nfs_server *server, struct rpc_cred *cred)
/*
* Note: must be called with clp->cl_sem held in order to prevent races
* with reboot recovery!
*/
struct nfs4_state_owner *nfs4_get_state_owner(struct nfs_server *server, struct rpc_cred *cred)
{
struct nfs4_client *clp = server->nfs4_state;
struct nfs4_state_owner *sp, *new;
......@@ -254,23 +303,23 @@ nfs4_get_state_owner(struct nfs_server *server, struct rpc_cred *cred)
new->so_client = clp;
new->so_id = nfs4_alloc_lockowner_id(clp);
new->so_cred = cred;
new->so_generation = clp->cl_generation;
sp = new;
new = NULL;
}
spin_unlock(&clp->cl_lock);
if (new)
kfree(new);
if (sp) {
if (!test_bit(NFS4CLNT_OK, &clp->cl_state))
nfs4_wait_clnt_recover(server->client, clp);
} else
put_rpccred(cred);
return sp;
if (sp != NULL)
return sp;
put_rpccred(cred);
return NULL;
}
void
nfs4_put_state_owner(struct nfs4_state_owner *sp)
/*
* Must be called with clp->cl_sem held in order to avoid races
* with state recovery...
*/
void nfs4_put_state_owner(struct nfs4_state_owner *sp)
{
struct nfs4_client *clp = sp->so_client;
struct rpc_cred *cred = sp->so_cred;
......@@ -330,8 +379,6 @@ __nfs4_find_state(struct inode *inode, struct rpc_cred *cred, mode_t mode)
continue;
if ((state->state & mode) != mode)
continue;
/* Add the state to the head of the inode's list */
list_move(&state->inode_states, &nfsi->open_states);
atomic_inc(&state->count);
if (mode & FMODE_READ)
state->nreaders++;
......@@ -353,8 +400,6 @@ __nfs4_find_state_byowner(struct inode *inode, struct nfs4_state_owner *owner)
if (state->nreaders == 0 && state->nwriters == 0)
continue;
if (state->owner == owner) {
/* Add the state to the head of the inode's list */
list_move(&state->inode_states, &nfsi->open_states);
atomic_inc(&state->count);
return state;
}
......@@ -411,51 +456,40 @@ nfs4_get_open_state(struct inode *inode, struct nfs4_state_owner *owner)
return state;
}
static void
__nfs4_put_open_state(struct nfs4_state *state)
/*
* Beware! Caller must be holding exactly one
* reference to clp->cl_sem and owner->so_sema!
*/
void nfs4_put_open_state(struct nfs4_state *state)
{
struct inode *inode = state->inode;
struct nfs4_state_owner *owner = state->owner;
int status = 0;
if (!atomic_dec_and_lock(&state->count, &inode->i_lock)) {
up(&owner->so_sema);
if (!atomic_dec_and_lock(&state->count, &inode->i_lock))
return;
}
if (!list_empty(&state->inode_states))
list_del(&state->inode_states);
spin_unlock(&inode->i_lock);
list_del(&state->open_states);
if (state->state != 0) {
do {
status = nfs4_do_close(inode, state);
if (!status)
break;
up(&owner->so_sema);
status = nfs4_handle_error(NFS_SERVER(inode), status);
down(&owner->so_sema);
} while (!status);
}
up(&owner->so_sema);
BUG_ON (state->state != 0);
nfs4_free_open_state(state);
nfs4_put_state_owner(owner);
}
void
nfs4_put_open_state(struct nfs4_state *state)
{
down(&state->owner->so_sema);
__nfs4_put_open_state(state);
}
void
nfs4_close_state(struct nfs4_state *state, mode_t mode)
/*
* Beware! Caller must be holding no references to clp->cl_sem!
* of owner->so_sema!
*/
void nfs4_close_state(struct nfs4_state *state, mode_t mode)
{
struct inode *inode = state->inode;
struct nfs4_state_owner *owner = state->owner;
struct nfs4_client *clp = owner->so_client;
int newstate;
int status = 0;
atomic_inc(&owner->so_count);
down_read(&clp->cl_sem);
down(&owner->so_sema);
/* Protect against nfs4_find_state() */
spin_lock(&inode->i_lock);
......@@ -466,29 +500,24 @@ nfs4_close_state(struct nfs4_state *state, mode_t mode)
if (state->nwriters == 0 && state->nreaders == 0)
list_del_init(&state->inode_states);
spin_unlock(&inode->i_lock);
do {
newstate = 0;
if (state->state == 0)
break;
newstate = 0;
if (state->state != 0) {
if (state->nreaders)
newstate |= FMODE_READ;
if (state->nwriters)
newstate |= FMODE_WRITE;
if (state->state == newstate)
break;
goto out;
if (newstate != 0)
status = nfs4_do_downgrade(inode, state, newstate);
else
status = nfs4_do_close(inode, state);
if (!status) {
state->state = newstate;
break;
}
up(&owner->so_sema);
status = nfs4_handle_error(NFS_SERVER(inode), status);
down(&owner->so_sema);
} while (!status);
__nfs4_put_open_state(state);
}
out:
nfs4_put_open_state(state);
up(&owner->so_sema);
nfs4_put_state_owner(owner);
up_read(&clp->cl_sem);
}
/*
......@@ -524,8 +553,7 @@ nfs4_find_lock_state(struct nfs4_state *state, fl_owner_t fl_owner)
*
* The caller must be holding state->lock_sema
*/
struct nfs4_lock_state *
nfs4_alloc_lock_state(struct nfs4_state *state, fl_owner_t fl_owner)
static struct nfs4_lock_state *nfs4_alloc_lock_state(struct nfs4_state *state, fl_owner_t fl_owner)
{
struct nfs4_lock_state *lsp;
struct nfs4_client *clp = state->owner->so_client;
......@@ -533,12 +561,12 @@ nfs4_alloc_lock_state(struct nfs4_state *state, fl_owner_t fl_owner)
lsp = kmalloc(sizeof(*lsp), GFP_KERNEL);
if (lsp == NULL)
return NULL;
lsp->ls_flags = 0;
lsp->ls_seqid = 0; /* arbitrary */
lsp->ls_id = -1;
memset(lsp->ls_stateid.data, 0, sizeof(lsp->ls_stateid.data));
atomic_set(&lsp->ls_count, 1);
lsp->ls_owner = fl_owner;
lsp->ls_parent = state;
INIT_LIST_HEAD(&lsp->ls_locks);
spin_lock(&clp->cl_lock);
lsp->ls_id = nfs4_alloc_lockowner_id(clp);
......@@ -546,6 +574,22 @@ nfs4_alloc_lock_state(struct nfs4_state *state, fl_owner_t fl_owner)
return lsp;
}
/*
* Return a compatible lock_state. If no initialized lock_state structure
* exists, return an uninitialized one.
*
* The caller must be holding state->lock_sema and clp->cl_sem
*/
struct nfs4_lock_state *nfs4_get_lock_state(struct nfs4_state *state, fl_owner_t owner)
{
struct nfs4_lock_state * lsp;
lsp = nfs4_find_lock_state(state, owner);
if (lsp == NULL)
lsp = nfs4_alloc_lock_state(state, owner);
return lsp;
}
/*
* Byte-range lock aware utility to initialize the stateid of read/write
* requests.
......@@ -567,10 +611,9 @@ nfs4_copy_stateid(nfs4_stateid *dst, struct nfs4_state *state, fl_owner_t fl_own
}
/*
* Called with state->lock_sema held.
* Called with state->lock_sema and clp->cl_sem held.
*/
void
nfs4_increment_lock_seqid(int status, struct nfs4_lock_state *lsp)
void nfs4_increment_lock_seqid(int status, struct nfs4_lock_state *lsp)
{
if (status == NFS_OK || seqid_mutating_err(-status))
lsp->ls_seqid++;
......@@ -597,13 +640,11 @@ nfs4_check_unlock(struct file_lock *fl, struct file_lock *request)
/*
* Post an initialized lock_state on the state->lock_states list.
*/
void
nfs4_notify_setlk(struct inode *inode, struct file_lock *request, struct nfs4_lock_state *lsp)
void nfs4_notify_setlk(struct nfs4_state *state, struct file_lock *request, struct nfs4_lock_state *lsp)
{
struct nfs4_state *state = lsp->ls_parent;
if (!list_empty(&lsp->ls_locks))
return;
atomic_inc(&lsp->ls_count);
write_lock(&state->state_lock);
list_add(&lsp->ls_locks, &state->lock_states);
set_bit(LK_STATE_IN_USE, &state->flags);
......@@ -620,9 +661,9 @@ nfs4_notify_setlk(struct inode *inode, struct file_lock *request, struct nfs4_lo
*
*/
void
nfs4_notify_unlck(struct inode *inode, struct file_lock *request, struct nfs4_lock_state *lsp)
nfs4_notify_unlck(struct nfs4_state *state, struct file_lock *request, struct nfs4_lock_state *lsp)
{
struct nfs4_state *state = lsp->ls_parent;
struct inode *inode = state->inode;
struct file_lock *fl;
for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
......@@ -640,6 +681,7 @@ nfs4_notify_unlck(struct inode *inode, struct file_lock *request, struct nfs4_lo
if (list_empty(&state->lock_states))
clear_bit(LK_STATE_IN_USE, &state->flags);
write_unlock(&state->state_lock);
nfs4_put_lock_state(lsp);
}
/*
......@@ -651,20 +693,18 @@ nfs4_put_lock_state(struct nfs4_lock_state *lsp)
{
if (!atomic_dec_and_test(&lsp->ls_count))
return;
if (!list_empty(&lsp->ls_locks))
return;
BUG_ON (!list_empty(&lsp->ls_locks));
kfree(lsp);
}
/*
* Called with sp->so_sema held.
* Called with sp->so_sema and clp->cl_sem held.
*
* Increment the seqid if the OPEN/OPEN_DOWNGRADE/CLOSE succeeded, or
* failed with a seqid incrementing error -
* see comments nfs_fs.h:seqid_mutating_error()
*/
void
nfs4_increment_seqid(int status, struct nfs4_state_owner *sp)
void nfs4_increment_seqid(int status, struct nfs4_state_owner *sp)
{
if (status == NFS_OK || seqid_mutating_err(-status))
sp->so_seqid++;
......@@ -693,21 +733,14 @@ nfs4_recover_state(void *data)
init_completion(&args.complete);
down_read(&clp->cl_sem);
if (test_and_set_bit(NFS4CLNT_SETUP_STATE, &clp->cl_state))
goto out_failed;
if (kernel_thread(reclaimer, &args, CLONE_KERNEL) < 0)
goto out_failed_clear;
wait_for_completion(&args.complete);
return;
out_failed_clear:
smp_mb__before_clear_bit();
clear_bit(NFS4CLNT_SETUP_STATE, &clp->cl_state);
smp_mb__after_clear_bit();
set_bit(NFS4CLNT_OK, &clp->cl_state);
wake_up_all(&clp->cl_waitq);
rpc_wake_up(&clp->cl_rpcwaitq);
out_failed:
up_read(&clp->cl_sem);
}
/*
......@@ -718,24 +751,66 @@ nfs4_schedule_state_recovery(struct nfs4_client *clp)
{
if (!clp)
return;
smp_mb__before_clear_bit();
clear_bit(NFS4CLNT_OK, &clp->cl_state);
smp_mb__after_clear_bit();
schedule_work(&clp->cl_recoverd);
if (test_and_clear_bit(NFS4CLNT_OK, &clp->cl_state))
schedule_work(&clp->cl_recoverd);
}
static int
nfs4_reclaim_open_state(struct nfs4_state_owner *sp)
static int nfs4_reclaim_locks(struct nfs4_state *state)
{
struct inode *inode = state->inode;
struct file_lock *fl;
int status = 0;
for (fl = inode->i_flock; fl != 0; fl = fl->fl_next) {
if (!(fl->fl_flags & FL_POSIX))
continue;
if (((struct nfs_open_context *)fl->fl_file->private_data)->state != state)
continue;
status = nfs4_lock_reclaim(state, fl);
if (status >= 0)
continue;
switch (status) {
default:
printk(KERN_ERR "%s: unhandled error %d. Zeroing state\n",
__FUNCTION__, status);
case -NFS4ERR_EXPIRED:
case -NFS4ERR_NO_GRACE:
case -NFS4ERR_RECLAIM_BAD:
case -NFS4ERR_RECLAIM_CONFLICT:
/* kill_proc(fl->fl_owner, SIGLOST, 1); */
break;
case -NFS4ERR_STALE_CLIENTID:
goto out_err;
}
}
return 0;
out_err:
return status;
}
static int nfs4_reclaim_open_state(struct nfs4_state_owner *sp)
{
struct nfs4_state *state;
struct nfs4_lock_state *lock;
int status = 0;
list_for_each_entry(state, &sp->so_states, open_states) {
if (state->state == 0)
continue;
status = nfs4_open_reclaim(sp, state);
if (status >= 0)
list_for_each_entry(lock, &state->lock_states, ls_locks)
lock->ls_flags &= ~NFS_LOCK_INITIALIZED;
if (status >= 0) {
status = nfs4_reclaim_locks(state);
if (status < 0)
goto out_err;
list_for_each_entry(lock, &state->lock_states, ls_locks) {
if (!(lock->ls_flags & NFS_LOCK_INITIALIZED))
printk("%s: Lock reclaim failed!\n",
__FUNCTION__);
}
continue;
}
switch (status) {
default:
printk(KERN_ERR "%s: unhandled error %d. Zeroing state\n",
......@@ -762,75 +837,57 @@ nfs4_reclaim_open_state(struct nfs4_state_owner *sp)
return status;
}
static int
reclaimer(void *ptr)
static int reclaimer(void *ptr)
{
struct reclaimer_args *args = (struct reclaimer_args *)ptr;
struct nfs4_client *clp = args->clp;
struct nfs4_state_owner *sp;
int generation;
int status;
int status = 0;
daemonize("%u.%u.%u.%u-reclaim", NIPQUAD(clp->cl_addr));
allow_signal(SIGKILL);
atomic_inc(&clp->cl_count);
complete(&args->complete);
/* Ensure exclusive access to NFSv4 state */
lock_kernel();
down_write(&clp->cl_sem);
/* Are there any NFS mounts out there? */
if (list_empty(&clp->cl_superblocks))
goto out;
if (!test_bit(NFS4CLNT_NEW, &clp->cl_state)) {
status = nfs4_proc_renew(clp);
if (status == 0) {
set_bit(NFS4CLNT_OK, &clp->cl_state);
goto out;
}
}
status = nfs4_proc_setclientid(clp, 0, 0);
if (status)
goto out_error;
status = nfs4_proc_setclientid_confirm(clp);
restart_loop:
status = nfs4_proc_renew(clp);
if (status == 0 || status == -NFS4ERR_CB_PATH_DOWN)
goto out;
status = nfs4_init_client(clp);
if (status)
goto out_error;
generation = ++(clp->cl_generation);
clear_bit(NFS4CLNT_NEW, &clp->cl_state);
set_bit(NFS4CLNT_OK, &clp->cl_state);
up_read(&clp->cl_sem);
nfs4_schedule_state_renewal(clp);
restart_loop:
spin_lock(&clp->cl_lock);
/* Mark all delagations for reclaim */
nfs_delegation_mark_reclaim(clp);
/* Note: list is protected by exclusive lock on cl->cl_sem */
list_for_each_entry(sp, &clp->cl_state_owners, so_list) {
if (sp->so_generation - generation >= 0)
continue;
atomic_inc(&sp->so_count);
spin_unlock(&clp->cl_lock);
down(&sp->so_sema);
if (sp->so_generation - generation < 0) {
smp_rmb();
sp->so_generation = clp->cl_generation;
status = nfs4_reclaim_open_state(sp);
}
up(&sp->so_sema);
nfs4_put_state_owner(sp);
status = nfs4_reclaim_open_state(sp);
if (status < 0) {
if (status == -NFS4ERR_STALE_CLIENTID)
nfs4_schedule_state_recovery(clp);
goto out;
goto restart_loop;
goto out_error;
}
goto restart_loop;
}
spin_unlock(&clp->cl_lock);
nfs_delegation_reap_unclaimed(clp);
out:
smp_mb__before_clear_bit();
clear_bit(NFS4CLNT_SETUP_STATE, &clp->cl_state);
smp_mb__after_clear_bit();
set_bit(NFS4CLNT_OK, &clp->cl_state);
up_write(&clp->cl_sem);
unlock_kernel();
wake_up_all(&clp->cl_waitq);
rpc_wake_up(&clp->cl_rpcwaitq);
if (status == -NFS4ERR_CB_PATH_DOWN)
nfs_handle_cb_pathdown(clp);
nfs4_put_client(clp);
return 0;
out_error:
printk(KERN_WARNING "Error: state recovery failed on NFSv4 server %u.%u.%u.%u\n",
NIPQUAD(clp->cl_addr.s_addr));
up_read(&clp->cl_sem);
printk(KERN_WARNING "Error: state recovery failed on NFSv4 server %u.%u.%u.%u with error %d\n",
NIPQUAD(clp->cl_addr.s_addr), -status);
goto out;
}
......
......@@ -84,6 +84,7 @@ static int nfs_stat_to_errno(int);
((3+NFS4_FHSIZE) >> 2))
#define encode_getattr_maxsz (op_encode_hdr_maxsz + 3)
#define nfs4_name_maxsz (1 + ((3 + NFS4_MAXNAMLEN) >> 2))
#define nfs4_path_maxsz (1 + ((3 + NFS4_MAXPATHLEN) >> 2))
#define nfs4_fattr_bitmap_maxsz (36 + 2 * nfs4_name_maxsz)
#define decode_getattr_maxsz (op_decode_hdr_maxsz + 3 + \
nfs4_fattr_bitmap_maxsz)
......@@ -118,10 +119,17 @@ static int nfs_stat_to_errno(int);
#define encode_link_maxsz (op_encode_hdr_maxsz + \
nfs4_name_maxsz)
#define decode_link_maxsz (op_decode_hdr_maxsz + 5)
#define encode_symlink_maxsz (op_encode_hdr_maxsz + \
1 + nfs4_name_maxsz + \
nfs4_path_maxsz + \
nfs4_fattr_bitmap_maxsz)
#define decode_symlink_maxsz (op_decode_hdr_maxsz + 8)
#define encode_create_maxsz (op_encode_hdr_maxsz + \
2 + 2 * nfs4_name_maxsz + \
2 + nfs4_name_maxsz + \
nfs4_fattr_bitmap_maxsz)
#define decode_create_maxsz (op_decode_hdr_maxsz + 8)
#define encode_delegreturn_maxsz (op_encode_hdr_maxsz + 4)
#define decode_delegreturn_maxsz (op_decode_hdr_maxsz)
#define NFS4_enc_compound_sz (1024) /* XXX: large enough? */
#define NFS4_dec_compound_sz (1024) /* XXX: large enough? */
#define NFS4_enc_read_sz (compound_encode_hdr_maxsz + \
......@@ -172,16 +180,14 @@ static int nfs_stat_to_errno(int);
#define NFS4_dec_open_confirm_sz (compound_decode_hdr_maxsz + \
decode_putfh_maxsz + \
op_decode_hdr_maxsz + 4)
#define NFS4_enc_open_reclaim_sz (compound_encode_hdr_maxsz + \
#define NFS4_enc_open_noattr_sz (compound_encode_hdr_maxsz + \
encode_putfh_maxsz + \
op_encode_hdr_maxsz + \
11 + \
encode_getattr_maxsz)
#define NFS4_dec_open_reclaim_sz (compound_decode_hdr_maxsz + \
11)
#define NFS4_dec_open_noattr_sz (compound_decode_hdr_maxsz + \
decode_putfh_maxsz + \
op_decode_hdr_maxsz + \
4 + 5 + 2 + 3 + \
decode_getattr_maxsz)
4 + 5 + 2 + 3)
#define NFS4_enc_open_downgrade_sz \
(compound_encode_hdr_maxsz + \
encode_putfh_maxsz + \
......@@ -313,6 +319,16 @@ static int nfs_stat_to_errno(int);
decode_savefh_maxsz + \
decode_putfh_maxsz + \
decode_link_maxsz)
#define NFS4_enc_symlink_sz (compound_encode_hdr_maxsz + \
encode_putfh_maxsz + \
encode_symlink_maxsz + \
encode_getattr_maxsz + \
encode_getfh_maxsz)
#define NFS4_dec_symlink_sz (compound_decode_hdr_maxsz + \
decode_putfh_maxsz + \
decode_symlink_maxsz + \
decode_getattr_maxsz + \
decode_getfh_maxsz)
#define NFS4_enc_create_sz (compound_encode_hdr_maxsz + \
encode_putfh_maxsz + \
encode_create_maxsz + \
......@@ -339,6 +355,11 @@ static int nfs_stat_to_errno(int);
encode_getattr_maxsz)
#define NFS4_dec_server_caps_sz (compound_decode_hdr_maxsz + \
decode_getattr_maxsz)
#define NFS4_enc_delegreturn_sz (compound_encode_hdr_maxsz + \
encode_putfh_maxsz + \
encode_delegreturn_maxsz)
#define NFS4_dec_delegreturn_sz (compound_decode_hdr_maxsz + \
decode_delegreturn_maxsz)
static struct {
unsigned int mode;
......@@ -388,6 +409,15 @@ struct compound_hdr {
BUG_ON(!p); \
} while (0)
static void encode_string(struct xdr_stream *xdr, unsigned int len, const char *str)
{
uint32_t *p;
p = xdr_reserve_space(xdr, 4 + len);
BUG_ON(p == NULL);
xdr_encode_opaque(p, str, len);
}
static int encode_compound_hdr(struct xdr_stream *xdr, struct compound_hdr *hdr)
{
uint32_t *p;
......@@ -402,6 +432,15 @@ static int encode_compound_hdr(struct xdr_stream *xdr, struct compound_hdr *hdr)
return 0;
}
static void encode_nfs4_verifier(struct xdr_stream *xdr, const nfs4_verifier *verf)
{
uint32_t *p;
p = xdr_reserve_space(xdr, NFS4_VERIFIER_SIZE);
BUG_ON(p == NULL);
xdr_encode_opaque_fixed(p, verf->data, NFS4_VERIFIER_SIZE);
}
static int encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const struct nfs_server *server)
{
char owner_name[IDMAP_NAMESZ];
......@@ -742,19 +781,12 @@ static int encode_lookup(struct xdr_stream *xdr, const struct qstr *name)
return 0;
}
static int encode_open(struct xdr_stream *xdr, const struct nfs_openargs *arg)
static void encode_share_access(struct xdr_stream *xdr, int open_flags)
{
int status;
uint32_t *p;
/*
* opcode 4, seqid 4, share_access 4, share_deny 4, clientid 8, ownerlen 4,
* owner 4, opentype 4 = 36
*/
RESERVE_SPACE(36);
WRITE32(OP_OPEN);
WRITE32(arg->seqid);
switch (arg->share_access) {
RESERVE_SPACE(8);
switch (open_flags & (FMODE_READ|FMODE_WRITE)) {
case FMODE_READ:
WRITE32(NFS4_SHARE_ACCESS_READ);
break;
......@@ -767,84 +799,135 @@ static int encode_open(struct xdr_stream *xdr, const struct nfs_openargs *arg)
default:
BUG();
}
WRITE32(0); /* for linux, share_deny = 0 always */
WRITE32(0); /* for linux, share_deny = 0 always */
}
static inline void encode_openhdr(struct xdr_stream *xdr, const struct nfs_openargs *arg)
{
uint32_t *p;
/*
* opcode 4, seqid 4, share_access 4, share_deny 4, clientid 8, ownerlen 4,
* owner 4 = 32
*/
RESERVE_SPACE(8);
WRITE32(OP_OPEN);
WRITE32(arg->seqid);
encode_share_access(xdr, arg->open_flags);
RESERVE_SPACE(16);
WRITE64(arg->clientid);
WRITE32(4);
WRITE32(arg->id);
WRITE32(arg->opentype);
}
if (arg->opentype == NFS4_OPEN_CREATE) {
if (arg->createmode == NFS4_CREATE_EXCLUSIVE) {
RESERVE_SPACE(12);
WRITE32(arg->createmode);
WRITEMEM(arg->u.verifier.data, sizeof(arg->u.verifier.data));
}
else if (arg->u.attrs) {
RESERVE_SPACE(4);
WRITE32(arg->createmode);
if ((status = encode_attrs(xdr, arg->u.attrs, arg->server)))
return status;
}
else {
RESERVE_SPACE(12);
WRITE32(arg->createmode);
WRITE32(0);
WRITE32(0);
}
static inline void encode_createmode(struct xdr_stream *xdr, const struct nfs_openargs *arg)
{
uint32_t *p;
RESERVE_SPACE(4);
switch(arg->open_flags & O_EXCL) {
case 0:
WRITE32(NFS4_CREATE_UNCHECKED);
encode_attrs(xdr, arg->u.attrs, arg->server);
break;
default:
WRITE32(NFS4_CREATE_EXCLUSIVE);
encode_nfs4_verifier(xdr, &arg->u.verifier);
}
}
RESERVE_SPACE(8 + arg->name->len);
WRITE32(NFS4_OPEN_CLAIM_NULL);
WRITE32(arg->name->len);
WRITEMEM(arg->name->name, arg->name->len);
static void encode_opentype(struct xdr_stream *xdr, const struct nfs_openargs *arg)
{
uint32_t *p;
return 0;
RESERVE_SPACE(4);
switch (arg->open_flags & O_CREAT) {
case 0:
WRITE32(NFS4_OPEN_NOCREATE);
break;
default:
BUG_ON(arg->claim != NFS4_OPEN_CLAIM_NULL);
WRITE32(NFS4_OPEN_CREATE);
encode_createmode(xdr, arg);
}
}
static int encode_open_confirm(struct xdr_stream *xdr, const struct nfs_open_confirmargs *arg)
static inline void encode_delegation_type(struct xdr_stream *xdr, int delegation_type)
{
uint32_t *p;
RESERVE_SPACE(8+sizeof(arg->stateid.data));
WRITE32(OP_OPEN_CONFIRM);
WRITEMEM(arg->stateid.data, sizeof(arg->stateid.data));
WRITE32(arg->seqid);
RESERVE_SPACE(4);
switch (delegation_type) {
case 0:
WRITE32(NFS4_OPEN_DELEGATE_NONE);
break;
case FMODE_READ:
WRITE32(NFS4_OPEN_DELEGATE_READ);
break;
case FMODE_WRITE|FMODE_READ:
WRITE32(NFS4_OPEN_DELEGATE_WRITE);
break;
default:
BUG();
}
}
return 0;
static inline void encode_claim_null(struct xdr_stream *xdr, const struct qstr *name)
{
uint32_t *p;
RESERVE_SPACE(4);
WRITE32(NFS4_OPEN_CLAIM_NULL);
encode_string(xdr, name->len, name->name);
}
static inline void encode_claim_previous(struct xdr_stream *xdr, int type)
{
uint32_t *p;
RESERVE_SPACE(4);
WRITE32(NFS4_OPEN_CLAIM_PREVIOUS);
encode_delegation_type(xdr, type);
}
static int encode_open_reclaim(struct xdr_stream *xdr, const struct nfs_open_reclaimargs *arg)
static inline void encode_claim_delegate_cur(struct xdr_stream *xdr, const struct qstr *name, const nfs4_stateid *stateid)
{
uint32_t *p;
/*
* opcode 4, seqid 4, share_access 4, share_deny 4, clientid 8, ownerlen 4,
* owner 4, opentype 4, claim 4, delegation_type 4 = 44
*/
RESERVE_SPACE(44);
WRITE32(OP_OPEN);
WRITE32(arg->seqid);
switch (arg->share_access) {
case FMODE_READ:
WRITE32(NFS4_SHARE_ACCESS_READ);
RESERVE_SPACE(4+sizeof(stateid->data));
WRITE32(NFS4_OPEN_CLAIM_DELEGATE_CUR);
WRITEMEM(stateid->data, sizeof(stateid->data));
encode_string(xdr, name->len, name->name);
}
static int encode_open(struct xdr_stream *xdr, const struct nfs_openargs *arg)
{
encode_openhdr(xdr, arg);
encode_opentype(xdr, arg);
switch (arg->claim) {
case NFS4_OPEN_CLAIM_NULL:
encode_claim_null(xdr, arg->name);
break;
case FMODE_WRITE:
WRITE32(NFS4_SHARE_ACCESS_WRITE);
case NFS4_OPEN_CLAIM_PREVIOUS:
encode_claim_previous(xdr, arg->u.delegation_type);
break;
case FMODE_READ|FMODE_WRITE:
WRITE32(NFS4_SHARE_ACCESS_BOTH);
case NFS4_OPEN_CLAIM_DELEGATE_CUR:
encode_claim_delegate_cur(xdr, arg->name, &arg->u.delegation);
break;
default:
BUG();
}
WRITE32(0); /* for linux, share_deny = 0 always */
WRITE64(arg->clientid);
WRITE32(4);
WRITE32(arg->id);
WRITE32(NFS4_OPEN_NOCREATE);
WRITE32(NFS4_OPEN_CLAIM_PREVIOUS);
WRITE32(NFS4_OPEN_DELEGATE_NONE);
return 0;
}
static int encode_open_confirm(struct xdr_stream *xdr, const struct nfs_open_confirmargs *arg)
{
uint32_t *p;
RESERVE_SPACE(8+sizeof(arg->stateid.data));
WRITE32(OP_OPEN_CONFIRM);
WRITEMEM(arg->stateid.data, sizeof(arg->stateid.data));
WRITE32(arg->seqid);
return 0;
}
......@@ -852,14 +935,11 @@ static int encode_open_downgrade(struct xdr_stream *xdr, const struct nfs_closea
{
uint32_t *p;
RESERVE_SPACE(16+sizeof(arg->stateid.data));
RESERVE_SPACE(8+sizeof(arg->stateid.data));
WRITE32(OP_OPEN_DOWNGRADE);
WRITEMEM(arg->stateid.data, sizeof(arg->stateid.data));
WRITE32(arg->seqid);
WRITE32(arg->share_access);
/* No deny modes */
WRITE32(0);
encode_share_access(xdr, arg->open_flags);
return 0;
}
......@@ -887,15 +967,15 @@ static int encode_putrootfh(struct xdr_stream *xdr)
return 0;
}
static void encode_stateid(struct xdr_stream *xdr, struct nfs4_state *state, fl_owner_t lockowner)
static void encode_stateid(struct xdr_stream *xdr, const struct nfs_open_context *ctx)
{
extern nfs4_stateid zero_stateid;
nfs4_stateid stateid;
uint32_t *p;
RESERVE_SPACE(16);
if (state != NULL) {
nfs4_copy_stateid(&stateid, state, lockowner);
if (ctx->state != NULL) {
nfs4_copy_stateid(&stateid, ctx->state, ctx->lockowner);
WRITEMEM(stateid.data, sizeof(stateid.data));
} else
WRITEMEM(zero_stateid.data, sizeof(zero_stateid.data));
......@@ -908,7 +988,7 @@ static int encode_read(struct xdr_stream *xdr, const struct nfs_readargs *args)
RESERVE_SPACE(4);
WRITE32(OP_READ);
encode_stateid(xdr, args->state, args->lockowner);
encode_stateid(xdr, args->context);
RESERVE_SPACE(12);
WRITE64(args->offset);
......@@ -927,7 +1007,7 @@ static int encode_readdir(struct xdr_stream *xdr, const struct nfs4_readdir_arg
WRITE32(OP_READDIR);
WRITE64(readdir->cookie);
WRITEMEM(readdir->verifier.data, sizeof(readdir->verifier.data));
WRITE32(readdir->count >> 5); /* meaningless "dircount" field */
WRITE32(readdir->count >> 1); /* We're not doing readdirplus */
WRITE32(readdir->count);
WRITE32(2);
WRITE32(FATTR4_WORD0_FILEID);
......@@ -1031,26 +1111,18 @@ static int encode_setattr(struct xdr_stream *xdr, const struct nfs_setattrargs *
static int encode_setclientid(struct xdr_stream *xdr, const struct nfs4_setclientid *setclientid)
{
uint32_t total_len;
uint32_t len1, len2, len3;
uint32_t *p;
len1 = strlen(setclientid->sc_name);
len2 = strlen(setclientid->sc_netid);
len3 = strlen(setclientid->sc_uaddr);
total_len = XDR_QUADLEN(len1) + XDR_QUADLEN(len2) + XDR_QUADLEN(len3);
total_len = (total_len << 2) + 24 + sizeof(setclientid->sc_verifier.data);
RESERVE_SPACE(total_len);
RESERVE_SPACE(4 + sizeof(setclientid->sc_verifier->data));
WRITE32(OP_SETCLIENTID);
WRITEMEM(setclientid->sc_verifier.data, sizeof(setclientid->sc_verifier.data));
WRITE32(len1);
WRITEMEM(setclientid->sc_name, len1);
WRITEMEM(setclientid->sc_verifier->data, sizeof(setclientid->sc_verifier->data));
encode_string(xdr, setclientid->sc_name_len, setclientid->sc_name);
RESERVE_SPACE(4);
WRITE32(setclientid->sc_prog);
WRITE32(len2);
WRITEMEM(setclientid->sc_netid, len2);
WRITE32(len3);
WRITEMEM(setclientid->sc_uaddr, len3);
encode_string(xdr, setclientid->sc_netid_len, setclientid->sc_netid);
encode_string(xdr, setclientid->sc_uaddr_len, setclientid->sc_uaddr);
RESERVE_SPACE(4);
WRITE32(setclientid->sc_cb_ident);
return 0;
......@@ -1075,7 +1147,7 @@ static int encode_write(struct xdr_stream *xdr, const struct nfs_writeargs *args
RESERVE_SPACE(4);
WRITE32(OP_WRITE);
encode_stateid(xdr, args->state, args->lockowner);
encode_stateid(xdr, args->context);
RESERVE_SPACE(16);
WRITE64(args->offset);
......@@ -1086,6 +1158,18 @@ static int encode_write(struct xdr_stream *xdr, const struct nfs_writeargs *args
return 0;
}
static int encode_delegreturn(struct xdr_stream *xdr, const nfs4_stateid *stateid)
{
uint32_t *p;
RESERVE_SPACE(20);
WRITE32(OP_DELEGRETURN);
WRITEMEM(stateid->data, sizeof(stateid->data));
return 0;
}
/*
* END OF "GENERIC" ENCODE ROUTINES.
*/
......@@ -1236,13 +1320,21 @@ static int nfs4_xdr_enc_create(struct rpc_rqst *req, uint32_t *p, const struct n
goto out;
if ((status = encode_create(&xdr, args)) != 0)
goto out;
if ((status = encode_getfattr(&xdr, args->bitmask)) != 0)
if ((status = encode_getfh(&xdr)) != 0)
goto out;
status = encode_getfh(&xdr);
status = encode_getfattr(&xdr, args->bitmask);
out:
return status;
}
/*
* Encode SYMLINK request
*/
static int nfs4_xdr_enc_symlink(struct rpc_rqst *req, uint32_t *p, const struct nfs4_create_arg *args)
{
return nfs4_xdr_enc_create(req, p, args);
}
/*
* Encode GETATTR request
*/
......@@ -1301,10 +1393,10 @@ static int nfs4_xdr_enc_open(struct rpc_rqst *req, uint32_t *p, struct nfs_opena
status = encode_open(&xdr, args);
if (status)
goto out;
status = encode_getfattr(&xdr, args->bitmask);
status = encode_getfh(&xdr);
if (status)
goto out;
status = encode_getfh(&xdr);
status = encode_getfattr(&xdr, args->bitmask);
out:
return status;
}
......@@ -1331,13 +1423,13 @@ static int nfs4_xdr_enc_open_confirm(struct rpc_rqst *req, uint32_t *p, struct n
}
/*
* Encode an OPEN request
* Encode an OPEN request with no attributes.
*/
static int nfs4_xdr_enc_open_reclaim(struct rpc_rqst *req, uint32_t *p, struct nfs_open_reclaimargs *args)
static int nfs4_xdr_enc_open_noattr(struct rpc_rqst *req, uint32_t *p, struct nfs_openargs *args)
{
struct xdr_stream xdr;
struct compound_hdr hdr = {
.nops = 3,
.nops = 2,
};
int status;
......@@ -1346,10 +1438,7 @@ static int nfs4_xdr_enc_open_reclaim(struct rpc_rqst *req, uint32_t *p, struct n
status = encode_putfh(&xdr, args->fh);
if (status)
goto out;
status = encode_open_reclaim(&xdr, args);
if (status)
goto out;
status = encode_getfattr(&xdr, args->bitmask);
status = encode_open(&xdr, args);
out:
return status;
}
......@@ -1715,6 +1804,24 @@ static int nfs4_xdr_enc_setclientid_confirm(struct rpc_rqst *req, uint32_t *p, s
return status;
}
/*
* DELEGRETURN request
*/
static int nfs4_xdr_enc_delegreturn(struct rpc_rqst *req, uint32_t *p, const struct nfs4_delegreturnargs *args)
{
struct xdr_stream xdr;
struct compound_hdr hdr = {
.nops = 2,
};
int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr);
if ((status = encode_putfh(&xdr, args->fhandle)) == 0)
status = encode_delegreturn(&xdr, args->stateid);
return status;
}
/*
* START OF "GENERIC" DECODE ROUTINES.
* These may look a little ugly since they are imported from a "generic"
......@@ -1749,6 +1856,17 @@ static int nfs4_xdr_enc_setclientid_confirm(struct rpc_rqst *req, uint32_t *p, s
} \
} while (0)
static int decode_opaque_inline(struct xdr_stream *xdr, uint32_t *len, char **string)
{
uint32_t *p;
READ_BUF(4);
READ32(*len);
READ_BUF(*len);
*string = (char *)p;
return 0;
}
static int decode_compound_hdr(struct xdr_stream *xdr, struct compound_hdr *hdr)
{
uint32_t *p;
......@@ -1785,6 +1903,17 @@ static int decode_op_hdr(struct xdr_stream *xdr, enum nfs_opnum4 expected)
return 0;
}
/* Dummy routine */
static int decode_ace(struct xdr_stream *xdr, void *ace, struct nfs4_client *clp)
{
uint32_t *p;
uint32_t strlen;
char *str;
READ_BUF(12);
return decode_opaque_inline(xdr, &strlen, &str);
}
static int decode_attr_bitmap(struct xdr_stream *xdr, uint32_t *bitmap)
{
uint32_t bmlen, *p;
......@@ -2717,10 +2846,56 @@ static int decode_lookup(struct xdr_stream *xdr)
return decode_op_hdr(xdr, OP_LOOKUP);
}
/* This is too sick! */
static int decode_space_limit(struct xdr_stream *xdr, u64 *maxsize)
{
uint32_t *p;
uint32_t limit_type, nblocks, blocksize;
READ_BUF(12);
READ32(limit_type);
switch (limit_type) {
case 1:
READ64(*maxsize);
break;
case 2:
READ32(nblocks);
READ32(blocksize);
*maxsize = (uint64_t)nblocks * (uint64_t)blocksize;
}
return 0;
}
static int decode_delegation(struct xdr_stream *xdr, struct nfs_openres *res)
{
uint32_t *p;
uint32_t delegation_type;
READ_BUF(4);
READ32(delegation_type);
if (delegation_type == NFS4_OPEN_DELEGATE_NONE) {
res->delegation_type = 0;
return 0;
}
READ_BUF(20);
COPYMEM(res->delegation.data, sizeof(res->delegation.data));
READ32(res->do_recall);
switch (delegation_type) {
case NFS4_OPEN_DELEGATE_READ:
res->delegation_type = FMODE_READ;
break;
case NFS4_OPEN_DELEGATE_WRITE:
res->delegation_type = FMODE_WRITE|FMODE_READ;
if (decode_space_limit(xdr, &res->maxsize) < 0)
return -EIO;
}
return decode_ace(xdr, NULL, res->server->nfs4_state);
}
static int decode_open(struct xdr_stream *xdr, struct nfs_openres *res)
{
uint32_t *p;
uint32_t bmlen, delegation_type;
uint32_t bmlen;
int status;
status = decode_op_hdr(xdr, OP_OPEN);
......@@ -2737,11 +2912,9 @@ static int decode_open(struct xdr_stream *xdr, struct nfs_openres *res)
if (bmlen > 10)
goto xdr_error;
READ_BUF((bmlen << 2) + 4);
READ_BUF(bmlen << 2);
p += bmlen;
READ32(delegation_type);
if (delegation_type == NFS4_OPEN_DELEGATE_NONE)
return 0;
return decode_delegation(xdr, res);
xdr_error:
printk(KERN_NOTICE "%s: xdr error!\n", __FUNCTION__);
return -EIO;
......@@ -2817,8 +2990,8 @@ static int decode_readdir(struct xdr_stream *xdr, struct rpc_rqst *req, struct n
struct kvec *iov = rcvbuf->head;
unsigned int nr, pglen = rcvbuf->page_len;
uint32_t *end, *entry, *p, *kaddr;
uint32_t len, attrlen, word;
int i, hdrlen, recvd, status;
uint32_t len, attrlen;
int hdrlen, recvd, status;
status = decode_op_hdr(xdr, OP_READDIR);
if (status)
......@@ -2839,42 +3012,24 @@ static int decode_readdir(struct xdr_stream *xdr, struct rpc_rqst *req, struct n
for (nr = 0; *p++; nr++) {
if (p + 3 > end)
goto short_pkt;
p += 2; /* cookie */
len = ntohl(*p++); /* filename length */
p += 2; /* cookie */
len = ntohl(*p++); /* filename length */
if (len > NFS4_MAXNAMLEN) {
printk(KERN_WARNING "NFS: giant filename in readdir (len 0x%x)\n", len);
goto err_unmap;
}
p += XDR_QUADLEN(len);
if (p + 1 > end)
goto short_pkt;
len = ntohl(*p++); /* bitmap length */
if (len > 10) {
printk(KERN_WARNING "NFS: giant bitmap in readdir (len 0x%x)\n", len);
goto err_unmap;
}
if (p + len + 1 > end)
goto short_pkt;
attrlen = 0;
for (i = 0; i < len; i++) {
word = ntohl(*p++);
if (!word)
continue;
else if (i == 0 && word == FATTR4_WORD0_FILEID) {
attrlen = 8;
continue;
}
printk(KERN_WARNING "NFS: unexpected bitmap word in readdir (0x%x)\n", word);
goto err_unmap;
}
if (ntohl(*p++) != attrlen) {
printk(KERN_WARNING "NFS: unexpected attrlen in readdir\n");
goto err_unmap;
}
p += XDR_QUADLEN(attrlen);
len = ntohl(*p++); /* bitmap length */
p += len;
if (p + 1 > end)
goto short_pkt;
attrlen = XDR_QUADLEN(ntohl(*p++));
p += attrlen; /* attributes */
if (p + 2 > end)
goto short_pkt;
entry = p;
}
if (!nr && (entry[0] != 0 || entry[1] == 0))
goto short_pkt;
......@@ -3048,6 +3203,11 @@ static int decode_write(struct xdr_stream *xdr, struct nfs_writeres *res)
return 0;
}
static int decode_delegreturn(struct xdr_stream *xdr)
{
return decode_op_hdr(xdr, OP_DELEGRETURN);
}
/*
* Decode OPEN_DOWNGRADE response
*/
......@@ -3214,13 +3374,23 @@ static int nfs4_xdr_dec_create(struct rpc_rqst *rqstp, uint32_t *p, struct nfs4_
goto out;
if ((status = decode_create(&xdr,&res->dir_cinfo)) != 0)
goto out;
if ((status = decode_getfattr(&xdr, res->fattr, res->server)) != 0)
if ((status = decode_getfh(&xdr, res->fh)) != 0)
goto out;
status = decode_getfh(&xdr, res->fh);
status = decode_getfattr(&xdr, res->fattr, res->server);
if (status == NFS4ERR_DELAY)
status = 0;
out:
return status;
}
/*
* Decode SYMLINK response
*/
static int nfs4_xdr_dec_symlink(struct rpc_rqst *rqstp, uint32_t *p, struct nfs4_create_res *res)
{
return nfs4_xdr_dec_create(rqstp, p, res);
}
/*
* Decode GETATTR response
*/
......@@ -3282,12 +3452,14 @@ static int nfs4_xdr_dec_open(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_ope
if (status)
goto out;
status = decode_open(&xdr, res);
if (status)
goto out;
status = decode_getfattr(&xdr, res->f_attr, res->server);
if (status)
goto out;
status = decode_getfh(&xdr, &res->fh);
if (status)
goto out;
status = decode_getfattr(&xdr, res->f_attr, res->server);
if (status == NFS4ERR_DELAY)
status = 0;
out:
return status;
}
......@@ -3314,9 +3486,9 @@ static int nfs4_xdr_dec_open_confirm(struct rpc_rqst *rqstp, uint32_t *p, struct
}
/*
* Decode OPEN_RECLAIM response
* Decode OPEN response
*/
static int nfs4_xdr_dec_open_reclaim(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_openres *res)
static int nfs4_xdr_dec_open_noattr(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_openres *res)
{
struct xdr_stream xdr;
struct compound_hdr hdr;
......@@ -3330,9 +3502,6 @@ static int nfs4_xdr_dec_open_reclaim(struct rpc_rqst *rqstp, uint32_t *p, struct
if (status)
goto out;
status = decode_open(&xdr, res);
if (status)
goto out;
status = decode_getfattr(&xdr, res->f_attr, res->server);
out:
return status;
}
......@@ -3357,6 +3526,8 @@ static int nfs4_xdr_dec_setattr(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_
if (status)
goto out;
status = decode_getfattr(&xdr, res->fattr, res->server);
if (status == NFS4ERR_DELAY)
status = 0;
out:
return status;
}
......@@ -3665,8 +3836,28 @@ static int nfs4_xdr_dec_setclientid_confirm(struct rpc_rqst *req, uint32_t *p, s
return status;
}
/*
* DELEGRETURN request
*/
static int nfs4_xdr_dec_delegreturn(struct rpc_rqst *rqstp, uint32_t *p, void *dummy)
{
struct xdr_stream xdr;
struct compound_hdr hdr;
int status;
xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
status = decode_compound_hdr(&xdr, &hdr);
if (status == 0) {
status = decode_putfh(&xdr);
if (status == 0)
status = decode_delegreturn(&xdr);
}
return status;
}
uint32_t *nfs4_decode_dirent(uint32_t *p, struct nfs_entry *entry, int plus)
{
uint32_t bitmap[1] = {0};
uint32_t len;
if (!*p++) {
......@@ -3689,11 +3880,17 @@ uint32_t *nfs4_decode_dirent(uint32_t *p, struct nfs_entry *entry, int plus)
*/
entry->ino = 1;
len = ntohl(*p++); /* bitmap length */
p += len;
len = ntohl(*p++); /* attribute buffer length */
if (len)
p = xdr_decode_hyper(p, &entry->ino);
len = ntohl(*p++); /* bitmap length */
if (len > 0) {
bitmap[0] = ntohl(*p);
p += len;
}
len = XDR_QUADLEN(ntohl(*p++)); /* attribute buffer length */
if (len > 0) {
if (bitmap[0] == FATTR4_WORD0_FILEID)
xdr_decode_hyper(p, &entry->ino);
p += len;
}
entry->eof = !p[0] && p[1];
return p;
......@@ -3756,7 +3953,7 @@ nfs_stat_to_errno(int stat)
if (nfs_errtbl[i].stat == stat)
return nfs_errtbl[i].errno;
}
if (stat < 0) {
if (stat <= 10000 || stat > 10100) {
/* The server is looney tunes. */
return ESERVERFAULT;
}
......@@ -3786,7 +3983,7 @@ struct rpc_procinfo nfs4_procedures[] = {
PROC(COMMIT, enc_commit, dec_commit),
PROC(OPEN, enc_open, dec_open),
PROC(OPEN_CONFIRM, enc_open_confirm, dec_open_confirm),
PROC(OPEN_RECLAIM, enc_open_reclaim, dec_open_reclaim),
PROC(OPEN_NOATTR, enc_open_noattr, dec_open_noattr),
PROC(OPEN_DOWNGRADE, enc_open_downgrade, dec_open_downgrade),
PROC(CLOSE, enc_close, dec_close),
PROC(SETATTR, enc_setattr, dec_setattr),
......@@ -3804,12 +4001,14 @@ struct rpc_procinfo nfs4_procedures[] = {
PROC(REMOVE, enc_remove, dec_remove),
PROC(RENAME, enc_rename, dec_rename),
PROC(LINK, enc_link, dec_link),
PROC(SYMLINK, enc_symlink, dec_symlink),
PROC(CREATE, enc_create, dec_create),
PROC(PATHCONF, enc_pathconf, dec_pathconf),
PROC(STATFS, enc_statfs, dec_statfs),
PROC(READLINK, enc_readlink, dec_readlink),
PROC(READDIR, enc_readdir, dec_readdir),
PROC(SERVER_CAPS, enc_server_caps, dec_server_caps),
PROC(DELEGRETURN, enc_delegreturn, dec_delegreturn),
};
struct rpc_version nfs_version4 = {
......
......@@ -495,10 +495,8 @@ static int __init root_nfs_get_handle(void)
if (status < 0)
printk(KERN_ERR "Root-NFS: Server returned error %d "
"while mounting %s\n", status, nfs_path);
else {
nfs_data.root.size = fh.size;
memcpy(nfs_data.root.data, fh.data, fh.size);
}
else
nfs_copy_fh(nfs_data.root, fh);
return status;
}
......
......@@ -21,11 +21,6 @@
#define NFS_PARANOIA 1
/*
* Spinlock
*/
spinlock_t nfs_wreq_lock = SPIN_LOCK_UNLOCKED;
static kmem_cache_t *nfs_page_cachep;
static inline struct nfs_page *
......@@ -36,7 +31,6 @@ nfs_page_alloc(void)
if (p) {
memset(p, 0, sizeof(*p));
INIT_LIST_HEAD(&p->wb_list);
init_waitqueue_head(&p->wb_wait);
}
return p;
}
......@@ -62,7 +56,7 @@ nfs_page_free(struct nfs_page *p)
* User should ensure it is safe to sleep in this function.
*/
struct nfs_page *
nfs_create_request(struct file *file, struct inode *inode,
nfs_create_request(struct nfs_open_context *ctx, struct inode *inode,
struct page *page,
unsigned int offset, unsigned int count)
{
......@@ -94,33 +88,38 @@ nfs_create_request(struct file *file, struct inode *inode,
req->wb_offset = offset;
req->wb_pgbase = offset;
req->wb_bytes = count;
req->wb_inode = inode;
req->wb_count = 1;
server->rpc_ops->request_init(req, file);
atomic_set(&req->wb_count, 1);
req->wb_context = get_nfs_open_context(ctx);
return req;
}
/**
* nfs_unlock_request - Unlock request and wake up sleepers.
* @req:
*/
void nfs_unlock_request(struct nfs_page *req)
{
if (!NFS_WBACK_BUSY(req)) {
printk(KERN_ERR "NFS: Invalid unlock attempted\n");
BUG();
}
smp_mb__before_clear_bit();
clear_bit(PG_BUSY, &req->wb_flags);
smp_mb__after_clear_bit();
wake_up_all(&req->wb_context->waitq);
nfs_release_request(req);
}
/**
* nfs_clear_request - Free up all resources allocated to the request
* @req:
*
* Release all resources associated with a write request after it
* Release page resources associated with a write request after it
* has completed.
*/
void nfs_clear_request(struct nfs_page *req)
{
if (req->wb_state)
req->wb_state = NULL;
/* Release struct file or cached credential */
if (req->wb_file) {
fput(req->wb_file);
req->wb_file = NULL;
}
if (req->wb_cred) {
put_rpccred(req->wb_cred);
req->wb_cred = NULL;
}
if (req->wb_page) {
page_cache_release(req->wb_page);
req->wb_page = NULL;
......@@ -137,12 +136,8 @@ void nfs_clear_request(struct nfs_page *req)
void
nfs_release_request(struct nfs_page *req)
{
spin_lock(&nfs_wreq_lock);
if (--req->wb_count) {
spin_unlock(&nfs_wreq_lock);
if (!atomic_dec_and_test(&req->wb_count))
return;
}
spin_unlock(&nfs_wreq_lock);
#ifdef NFS_PARANOIA
BUG_ON (!list_empty(&req->wb_list));
......@@ -151,6 +146,7 @@ nfs_release_request(struct nfs_page *req)
/* Release struct file or cached credential */
nfs_clear_request(req);
put_nfs_open_context(req->wb_context);
nfs_page_free(req);
}
......@@ -194,12 +190,12 @@ nfs_list_add_request(struct nfs_page *req, struct list_head *head)
int
nfs_wait_on_request(struct nfs_page *req)
{
struct inode *inode = req->wb_inode;
struct inode *inode = req->wb_context->dentry->d_inode;
struct rpc_clnt *clnt = NFS_CLIENT(inode);
if (!NFS_WBACK_BUSY(req))
return 0;
return nfs_wait_event(clnt, req->wb_wait, !NFS_WBACK_BUSY(req));
return nfs_wait_event(clnt, req->wb_context->waitq, !NFS_WBACK_BUSY(req));
}
/**
......@@ -224,7 +220,11 @@ nfs_coalesce_requests(struct list_head *head, struct list_head *dst,
req = nfs_list_entry(head->next);
if (prev) {
if (req->wb_cred != prev->wb_cred)
if (req->wb_context->cred != prev->wb_context->cred)
break;
if (req->wb_context->lockowner != prev->wb_context->lockowner)
break;
if (req->wb_context->state != prev->wb_context->state)
break;
if (req->wb_index != (prev->wb_index + 1))
break;
......@@ -254,7 +254,7 @@ nfs_coalesce_requests(struct list_head *head, struct list_head *dst,
* If the number of requests is set to 0, the entire address_space
* starting at index idx_start, is scanned.
* The requests are *not* checked to ensure that they form a contiguous set.
* You must be holding the nfs_wreq_lock when calling this function
* You must be holding the inode's req_lock when calling this function
*/
int
nfs_scan_list(struct list_head *head, struct list_head *dst,
......
......@@ -49,18 +49,6 @@
extern struct rpc_procinfo nfs_procedures[];
static struct rpc_cred *
nfs_cred(struct inode *inode, struct file *filp)
{
struct rpc_cred *cred = NULL;
if (filp)
cred = (struct rpc_cred *)filp->private_data;
if (!cred)
cred = NFS_I(inode)->mm_cred;
return cred;
}
/*
* Bare-bones access to getattr: this is for nfs_read_super.
*/
......@@ -99,14 +87,15 @@ nfs_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
* One function for each procedure in the NFS protocol.
*/
static int
nfs_proc_getattr(struct inode *inode, struct nfs_fattr *fattr)
nfs_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
struct nfs_fattr *fattr)
{
int status;
dprintk("NFS call getattr\n");
fattr->valid = 0;
status = rpc_call(NFS_CLIENT(inode), NFSPROC_GETATTR,
NFS_FH(inode), fattr, 0);
status = rpc_call(server->client, NFSPROC_GETATTR,
fhandle, fattr, 0);
dprintk("NFS reply getattr\n");
return status;
}
......@@ -167,8 +156,7 @@ nfs_proc_readlink(struct inode *inode, struct page *page)
return status;
}
static int
nfs_proc_read(struct nfs_read_data *rdata, struct file *filp)
static int nfs_proc_read(struct nfs_read_data *rdata)
{
int flags = rdata->flags;
struct inode * inode = rdata->inode;
......@@ -177,15 +165,14 @@ nfs_proc_read(struct nfs_read_data *rdata, struct file *filp)
.rpc_proc = &nfs_procedures[NFSPROC_READ],
.rpc_argp = &rdata->args,
.rpc_resp = &rdata->res,
.rpc_cred = rdata->cred,
};
int status;
dprintk("NFS call read %d @ %Ld\n", rdata->args.count,
(long long) rdata->args.offset);
fattr->valid = 0;
msg.rpc_cred = nfs_cred(inode, filp);
status = rpc_call_sync(NFS_CLIENT(inode), &msg, flags);
if (status >= 0) {
nfs_refresh_inode(inode, fattr);
/* Emulate the eof flag, which isn't normally needed in NFSv2
......@@ -198,8 +185,7 @@ nfs_proc_read(struct nfs_read_data *rdata, struct file *filp)
return status;
}
static int
nfs_proc_write(struct nfs_write_data *wdata, struct file *filp)
static int nfs_proc_write(struct nfs_write_data *wdata)
{
int flags = wdata->flags;
struct inode * inode = wdata->inode;
......@@ -208,13 +194,13 @@ nfs_proc_write(struct nfs_write_data *wdata, struct file *filp)
.rpc_proc = &nfs_procedures[NFSPROC_WRITE],
.rpc_argp = &wdata->args,
.rpc_resp = &wdata->res,
.rpc_cred = wdata->cred,
};
int status;
dprintk("NFS call write %d @ %Ld\n", wdata->args.count,
(long long) wdata->args.offset);
fattr->valid = 0;
msg.rpc_cred = nfs_cred(inode, filp);
status = rpc_call_sync(NFS_CLIENT(inode), &msg, flags);
if (status >= 0) {
nfs_refresh_inode(inode, fattr);
......@@ -400,6 +386,8 @@ nfs_proc_symlink(struct inode *dir, struct qstr *name, struct qstr *path,
};
int status;
if (path->len > NFS2_MAXPATHLEN)
return -ENAMETOOLONG;
dprintk("NFS call symlink %s -> %s\n", name->name, path->name);
fattr->valid = 0;
status = rpc_call(NFS_CLIENT(dir), NFSPROC_SYMLINK, &arg, NULL, 0);
......@@ -619,27 +607,6 @@ nfs_proc_commit_setup(struct nfs_write_data *data, int how)
BUG();
}
/*
* Set up the nfspage struct with the right credentials
*/
static void
nfs_request_init(struct nfs_page *req, struct file *filp)
{
req->wb_cred = get_rpccred(nfs_cred(req->wb_inode, filp));
}
static int
nfs_request_compatible(struct nfs_page *req, struct file *filp, struct page *page)
{
if (req->wb_file != filp)
return 0;
if (req->wb_page != page)
return 0;
if (req->wb_cred != nfs_file_cred(filp))
return 0;
return 1;
}
static int
nfs_proc_lock(struct file *filp, int cmd, struct file_lock *fl)
{
......@@ -680,7 +647,5 @@ struct nfs_rpc_ops nfs_v2_clientops = {
.commit_setup = nfs_proc_commit_setup,
.file_open = nfs_open,
.file_release = nfs_release,
.request_init = nfs_request_init,
.request_compatible = nfs_request_compatible,
.lock = nfs_proc_lock,
};
......@@ -91,8 +91,8 @@ int nfs_return_empty_page(struct page *page)
/*
* Read a page synchronously.
*/
static int
nfs_readpage_sync(struct file *file, struct inode *inode, struct page *page)
static int nfs_readpage_sync(struct nfs_open_context *ctx, struct inode *inode,
struct page *page)
{
unsigned int rsize = NFS_SERVER(inode)->rsize;
unsigned int count = PAGE_CACHE_SIZE;
......@@ -105,10 +105,11 @@ nfs_readpage_sync(struct file *file, struct inode *inode, struct page *page)
memset(rdata, 0, sizeof(*rdata));
rdata->flags = (IS_SWAPFILE(inode)? NFS_RPC_SWAPFLAGS : 0);
rdata->cred = ctx->cred;
rdata->inode = inode;
INIT_LIST_HEAD(&rdata->pages);
rdata->args.fh = NFS_FH(inode);
rdata->args.lockowner = current->files;
rdata->args.context = ctx;
rdata->args.pages = &page;
rdata->args.pgbase = 0UL;
rdata->args.count = rsize;
......@@ -134,7 +135,7 @@ nfs_readpage_sync(struct file *file, struct inode *inode, struct page *page)
rdata->args.count);
lock_kernel();
result = NFS_PROTO(inode)->read(rdata, file);
result = NFS_PROTO(inode)->read(rdata);
unlock_kernel();
/*
......@@ -169,8 +170,8 @@ nfs_readpage_sync(struct file *file, struct inode *inode, struct page *page)
return result;
}
static int
nfs_readpage_async(struct file *file, struct inode *inode, struct page *page)
static int nfs_readpage_async(struct nfs_open_context *ctx, struct inode *inode,
struct page *page)
{
LIST_HEAD(one_request);
struct nfs_page *new;
......@@ -179,7 +180,7 @@ nfs_readpage_async(struct file *file, struct inode *inode, struct page *page)
len = nfs_page_length(inode, page);
if (len == 0)
return nfs_return_empty_page(page);
new = nfs_create_request(file, inode, page, 0, len);
new = nfs_create_request(ctx, inode, page, 0, len);
if (IS_ERR(new)) {
unlock_page(page);
return PTR_ERR(new);
......@@ -202,8 +203,8 @@ static void nfs_readpage_release(struct nfs_page *req)
nfs_unlock_request(req);
dprintk("NFS: read done (%s/%Ld %d@%Ld)\n",
req->wb_inode->i_sb->s_id,
(long long)NFS_FILEID(req->wb_inode),
req->wb_context->dentry->d_inode->i_sb->s_id,
(long long)NFS_FILEID(req->wb_context->dentry->d_inode),
req->wb_bytes,
(long long)req_offset(req));
}
......@@ -217,16 +218,15 @@ static void nfs_read_rpcsetup(struct nfs_page *req, struct nfs_read_data *data,
struct inode *inode;
data->req = req;
data->inode = inode = req->wb_inode;
data->cred = req->wb_cred;
data->inode = inode = req->wb_context->dentry->d_inode;
data->cred = req->wb_context->cred;
data->args.fh = NFS_FH(inode);
data->args.offset = req_offset(req) + offset;
data->args.pgbase = req->wb_pgbase + offset;
data->args.pages = data->pagevec;
data->args.count = count;
data->args.lockowner = req->wb_lockowner;
data->args.state = req->wb_state;
data->args.context = req->wb_context;
data->res.fattr = &data->fattr;
data->res.count = count;
......@@ -396,7 +396,7 @@ nfs_pagein_list(struct list_head *head, int rpages)
while (!list_empty(head)) {
pages += nfs_coalesce_requests(head, &one_request, rpages);
req = nfs_list_entry(one_request.next);
error = nfs_pagein_one(&one_request, req->wb_inode);
error = nfs_pagein_one(&one_request, req->wb_context->dentry->d_inode);
if (error < 0)
break;
}
......@@ -500,9 +500,9 @@ void nfs_readpage_result(struct rpc_task *task)
* - The error flag is set for this page. This happens only when a
* previous async read operation failed.
*/
int
nfs_readpage(struct file *file, struct page *page)
int nfs_readpage(struct file *file, struct page *page)
{
struct nfs_open_context *ctx;
struct inode *inode = page->mapping->host;
int error;
......@@ -519,25 +519,33 @@ nfs_readpage(struct file *file, struct page *page)
if (error)
goto out_error;
if (file == NULL) {
ctx = nfs_find_open_context(inode, FMODE_READ);
if (ctx == NULL)
return -EBADF;
} else
ctx = get_nfs_open_context((struct nfs_open_context *)
file->private_data);
if (!IS_SYNC(inode)) {
error = nfs_readpage_async(file, inode, page);
error = nfs_readpage_async(ctx, inode, page);
goto out;
}
error = nfs_readpage_sync(file, inode, page);
error = nfs_readpage_sync(ctx, inode, page);
if (error < 0 && IS_SWAPFILE(inode))
printk("Aiee.. nfs swap-in of page failed!\n");
out:
put_nfs_open_context(ctx);
return error;
out_error:
unlock_page(page);
goto out;
return error;
}
struct nfs_readdesc {
struct list_head *head;
struct file *filp;
struct nfs_open_context *ctx;
};
static int
......@@ -552,7 +560,7 @@ readpage_async_filler(void *data, struct page *page)
len = nfs_page_length(inode, page);
if (len == 0)
return nfs_return_empty_page(page);
new = nfs_create_request(desc->filp, inode, page, 0, len);
new = nfs_create_request(desc->ctx, inode, page, 0, len);
if (IS_ERR(new)) {
SetPageError(page);
unlock_page(page);
......@@ -565,13 +573,11 @@ readpage_async_filler(void *data, struct page *page)
return 0;
}
int
nfs_readpages(struct file *filp, struct address_space *mapping,
int nfs_readpages(struct file *filp, struct address_space *mapping,
struct list_head *pages, unsigned nr_pages)
{
LIST_HEAD(head);
struct nfs_readdesc desc = {
.filp = filp,
.head = &head,
};
struct inode *inode = mapping->host;
......@@ -583,12 +589,20 @@ nfs_readpages(struct file *filp, struct address_space *mapping,
(long long)NFS_FILEID(inode),
nr_pages);
if (filp == NULL) {
desc.ctx = nfs_find_open_context(inode, FMODE_READ);
if (desc.ctx == NULL)
return -EBADF;
} else
desc.ctx = get_nfs_open_context((struct nfs_open_context *)
filp->private_data);
ret = read_cache_pages(mapping, pages, readpage_async_filler, &desc);
if (!list_empty(&head)) {
int err = nfs_pagein_list(&head, server->rpages);
if (!ret)
ret = err;
}
put_nfs_open_context(desc.ctx);
return ret;
}
......
......@@ -63,6 +63,8 @@
#include <linux/smp_lock.h>
#include <linux/mempool.h>
#include "delegation.h"
#define NFSDBG_FACILITY NFSDBG_PAGECACHE
#define MIN_POOL_WRITE (32)
......@@ -71,7 +73,8 @@
/*
* Local function declarations
*/
static struct nfs_page * nfs_update_request(struct file*, struct inode *,
static struct nfs_page * nfs_update_request(struct nfs_open_context*,
struct inode *,
struct page *,
unsigned int, unsigned int);
static void nfs_writeback_done_partial(struct nfs_write_data *, int);
......@@ -173,7 +176,7 @@ static void nfs_mark_uptodate(struct page *page, unsigned int base, unsigned int
* Write a page synchronously.
* Offset is the data offset within the page.
*/
static int nfs_writepage_sync(struct file *file, struct inode *inode,
static int nfs_writepage_sync(struct nfs_open_context *ctx, struct inode *inode,
struct page *page, unsigned int offset, unsigned int count,
int how)
{
......@@ -187,9 +190,10 @@ static int nfs_writepage_sync(struct file *file, struct inode *inode,
memset(wdata, 0, sizeof(*wdata));
wdata->flags = how;
wdata->cred = ctx->cred;
wdata->inode = inode;
wdata->args.fh = NFS_FH(inode);
wdata->args.lockowner = current->files;
wdata->args.context = ctx;
wdata->args.pages = &page;
wdata->args.stable = NFS_FILE_SYNC;
wdata->args.pgbase = offset;
......@@ -208,7 +212,7 @@ static int nfs_writepage_sync(struct file *file, struct inode *inode,
wdata->args.count = count;
wdata->args.offset = page_offset(page) + wdata->args.pgbase;
result = NFS_PROTO(inode)->write(wdata, file);
result = NFS_PROTO(inode)->write(wdata);
if (result < 0) {
/* Must mark the page invalid after I/O error */
......@@ -234,20 +238,19 @@ static int nfs_writepage_sync(struct file *file, struct inode *inode,
io_error:
nfs_end_data_update_defer(inode);
if (wdata->cred)
put_rpccred(wdata->cred);
kfree(wdata);
return written ? written : result;
}
static int nfs_writepage_async(struct file *file, struct inode *inode,
struct page *page, unsigned int offset, unsigned int count)
static int nfs_writepage_async(struct nfs_open_context *ctx,
struct inode *inode, struct page *page,
unsigned int offset, unsigned int count)
{
struct nfs_page *req;
int status;
req = nfs_update_request(file, inode, page, offset, count);
req = nfs_update_request(ctx, inode, page, offset, count);
status = (IS_ERR(req)) ? PTR_ERR(req) : 0;
if (status < 0)
goto out;
......@@ -274,6 +277,7 @@ static int wb_priority(struct writeback_control *wbc)
*/
int nfs_writepage(struct page *page, struct writeback_control *wbc)
{
struct nfs_open_context *ctx;
struct inode *inode = page->mapping->host;
unsigned long end_index;
unsigned offset = PAGE_CACHE_SIZE;
......@@ -308,16 +312,21 @@ int nfs_writepage(struct page *page, struct writeback_control *wbc)
if (page->index >= end_index+1 || !offset)
goto out;
do_it:
ctx = nfs_find_open_context(inode, FMODE_WRITE);
if (ctx == NULL) {
err = -EBADF;
goto out;
}
lock_kernel();
if (!IS_SYNC(inode) && inode_referenced) {
err = nfs_writepage_async(NULL, inode, page, 0, offset);
err = nfs_writepage_async(ctx, inode, page, 0, offset);
if (err >= 0) {
err = 0;
if (wbc->for_reclaim)
nfs_flush_inode(inode, 0, 0, FLUSH_STABLE);
}
} else {
err = nfs_writepage_sync(NULL, inode, page, 0,
err = nfs_writepage_sync(ctx, inode, page, 0,
offset, priority);
if (err >= 0) {
if (err != offset)
......@@ -326,6 +335,7 @@ int nfs_writepage(struct page *page, struct writeback_control *wbc)
}
}
unlock_kernel();
put_nfs_open_context(ctx);
out:
unlock_page(page);
if (inode_referenced)
......@@ -374,8 +384,7 @@ int nfs_writepages(struct address_space *mapping, struct writeback_control *wbc)
/*
* Insert a write request into an inode
*/
static inline int
nfs_inode_add_request(struct inode *inode, struct nfs_page *req)
static int nfs_inode_add_request(struct inode *inode, struct nfs_page *req)
{
struct nfs_inode *nfsi = NFS_I(inode);
int error;
......@@ -387,33 +396,33 @@ nfs_inode_add_request(struct inode *inode, struct nfs_page *req)
if (!nfsi->npages) {
igrab(inode);
nfs_begin_data_update(inode);
if (nfs_have_delegation(inode, FMODE_WRITE))
nfsi->change_attr++;
}
nfsi->npages++;
req->wb_count++;
atomic_inc(&req->wb_count);
return 0;
}
/*
* Insert a write request into an inode
*/
static void
nfs_inode_remove_request(struct nfs_page *req)
static void nfs_inode_remove_request(struct nfs_page *req)
{
struct nfs_inode *nfsi;
struct inode *inode;
struct inode *inode = req->wb_context->dentry->d_inode;
struct nfs_inode *nfsi = NFS_I(inode);
BUG_ON (!NFS_WBACK_BUSY(req));
spin_lock(&nfs_wreq_lock);
inode = req->wb_inode;
nfsi = NFS_I(inode);
spin_lock(&nfsi->req_lock);
radix_tree_delete(&nfsi->nfs_page_tree, req->wb_index);
nfsi->npages--;
if (!nfsi->npages) {
spin_unlock(&nfs_wreq_lock);
spin_unlock(&nfsi->req_lock);
nfs_end_data_update_defer(inode);
iput(inode);
} else
spin_unlock(&nfs_wreq_lock);
spin_unlock(&nfsi->req_lock);
nfs_clear_request(req);
nfs_release_request(req);
}
......@@ -429,7 +438,7 @@ _nfs_find_request(struct inode *inode, unsigned long index)
req = (struct nfs_page*)radix_tree_lookup(&nfsi->nfs_page_tree, index);
if (req)
req->wb_count++;
atomic_inc(&req->wb_count);
return req;
}
......@@ -437,10 +446,11 @@ static struct nfs_page *
nfs_find_request(struct inode *inode, unsigned long index)
{
struct nfs_page *req;
struct nfs_inode *nfsi = NFS_I(inode);
spin_lock(&nfs_wreq_lock);
spin_lock(&nfsi->req_lock);
req = _nfs_find_request(inode, index);
spin_unlock(&nfs_wreq_lock);
spin_unlock(&nfsi->req_lock);
return req;
}
......@@ -450,13 +460,13 @@ nfs_find_request(struct inode *inode, unsigned long index)
static void
nfs_mark_request_dirty(struct nfs_page *req)
{
struct inode *inode = req->wb_inode;
struct inode *inode = req->wb_context->dentry->d_inode;
struct nfs_inode *nfsi = NFS_I(inode);
spin_lock(&nfs_wreq_lock);
spin_lock(&nfsi->req_lock);
nfs_list_add_request(req, &nfsi->dirty);
nfsi->ndirty++;
spin_unlock(&nfs_wreq_lock);
spin_unlock(&nfsi->req_lock);
inc_page_state(nr_dirty);
mark_inode_dirty(inode);
}
......@@ -467,7 +477,7 @@ nfs_mark_request_dirty(struct nfs_page *req)
static inline int
nfs_dirty_request(struct nfs_page *req)
{
struct nfs_inode *nfsi = NFS_I(req->wb_inode);
struct nfs_inode *nfsi = NFS_I(req->wb_context->dentry->d_inode);
return !list_empty(&req->wb_list) && req->wb_list_head == &nfsi->dirty;
}
......@@ -478,13 +488,13 @@ nfs_dirty_request(struct nfs_page *req)
static void
nfs_mark_request_commit(struct nfs_page *req)
{
struct inode *inode = req->wb_inode;
struct inode *inode = req->wb_context->dentry->d_inode;
struct nfs_inode *nfsi = NFS_I(inode);
spin_lock(&nfs_wreq_lock);
spin_lock(&nfsi->req_lock);
nfs_list_add_request(req, &nfsi->commit);
nfsi->ncommit++;
spin_unlock(&nfs_wreq_lock);
spin_unlock(&nfsi->req_lock);
inc_page_state(nr_unstable);
mark_inode_dirty(inode);
}
......@@ -509,7 +519,7 @@ nfs_wait_on_requests(struct inode *inode, unsigned long idx_start, unsigned int
else
idx_end = idx_start + npages - 1;
spin_lock(&nfs_wreq_lock);
spin_lock(&nfsi->req_lock);
next = idx_start;
while (radix_tree_gang_lookup(&nfsi->nfs_page_tree, (void **)&req, next, 1)) {
if (req->wb_index > idx_end)
......@@ -519,16 +529,16 @@ nfs_wait_on_requests(struct inode *inode, unsigned long idx_start, unsigned int
if (!NFS_WBACK_BUSY(req))
continue;
req->wb_count++;
spin_unlock(&nfs_wreq_lock);
atomic_inc(&req->wb_count);
spin_unlock(&nfsi->req_lock);
error = nfs_wait_on_request(req);
nfs_release_request(req);
if (error < 0)
return error;
spin_lock(&nfs_wreq_lock);
spin_lock(&nfsi->req_lock);
res++;
}
spin_unlock(&nfs_wreq_lock);
spin_unlock(&nfsi->req_lock);
return res;
}
......@@ -619,11 +629,12 @@ static int nfs_wait_on_write_congestion(struct address_space *mapping, int intr)
*
* Note: Should always be called with the Page Lock held!
*/
static struct nfs_page *
nfs_update_request(struct file* file, struct inode *inode, struct page *page,
unsigned int offset, unsigned int bytes)
static struct nfs_page * nfs_update_request(struct nfs_open_context* ctx,
struct inode *inode, struct page *page,
unsigned int offset, unsigned int bytes)
{
struct nfs_server *server = NFS_SERVER(inode);
struct nfs_inode *nfsi = NFS_I(inode);
struct nfs_page *req, *new = NULL;
unsigned long rqend, end;
......@@ -635,19 +646,19 @@ nfs_update_request(struct file* file, struct inode *inode, struct page *page,
/* Loop over all inode entries and see if we find
* A request for the page we wish to update
*/
spin_lock(&nfs_wreq_lock);
spin_lock(&nfsi->req_lock);
req = _nfs_find_request(inode, page->index);
if (req) {
if (!nfs_lock_request_dontget(req)) {
int error;
spin_unlock(&nfs_wreq_lock);
spin_unlock(&nfsi->req_lock);
error = nfs_wait_on_request(req);
nfs_release_request(req);
if (error < 0)
return ERR_PTR(error);
continue;
}
spin_unlock(&nfs_wreq_lock);
spin_unlock(&nfsi->req_lock);
if (new)
nfs_release_request(new);
break;
......@@ -658,23 +669,19 @@ nfs_update_request(struct file* file, struct inode *inode, struct page *page,
nfs_lock_request_dontget(new);
error = nfs_inode_add_request(inode, new);
if (error) {
spin_unlock(&nfs_wreq_lock);
spin_unlock(&nfsi->req_lock);
nfs_unlock_request(new);
return ERR_PTR(error);
}
spin_unlock(&nfs_wreq_lock);
spin_unlock(&nfsi->req_lock);
nfs_mark_request_dirty(new);
return new;
}
spin_unlock(&nfs_wreq_lock);
spin_unlock(&nfsi->req_lock);
new = nfs_create_request(file, inode, page, offset, bytes);
new = nfs_create_request(ctx, inode, page, offset, bytes);
if (IS_ERR(new))
return new;
if (file) {
new->wb_file = file;
get_file(file);
}
}
/* We have a request for our page.
......@@ -684,7 +691,7 @@ nfs_update_request(struct file* file, struct inode *inode, struct page *page,
* request.
*/
rqend = req->wb_offset + req->wb_bytes;
if (req->wb_file != file
if (req->wb_context != ctx
|| req->wb_page != page
|| !nfs_dirty_request(req)
|| offset > rqend || end < req->wb_offset) {
......@@ -705,9 +712,9 @@ nfs_update_request(struct file* file, struct inode *inode, struct page *page,
return req;
}
int
nfs_flush_incompatible(struct file *file, struct page *page)
int nfs_flush_incompatible(struct file *file, struct page *page)
{
struct nfs_open_context *ctx = (struct nfs_open_context *)file->private_data;
struct inode *inode = page->mapping->host;
struct nfs_page *req;
int status = 0;
......@@ -721,7 +728,7 @@ nfs_flush_incompatible(struct file *file, struct page *page)
*/
req = nfs_find_request(inode, page->index);
if (req) {
if (!NFS_PROTO(inode)->request_compatible(req, file, page))
if (req->wb_page != page || ctx != req->wb_context)
status = nfs_wb_page(inode, page);
nfs_release_request(req);
}
......@@ -737,6 +744,7 @@ nfs_flush_incompatible(struct file *file, struct page *page)
int nfs_updatepage(struct file *file, struct page *page,
unsigned int offset, unsigned int count)
{
struct nfs_open_context *ctx = (struct nfs_open_context *)file->private_data;
struct dentry *dentry = file->f_dentry;
struct inode *inode = page->mapping->host;
struct nfs_page *req;
......@@ -747,7 +755,7 @@ int nfs_updatepage(struct file *file, struct page *page,
count, (long long)(page_offset(page) +offset));
if (IS_SYNC(inode)) {
status = nfs_writepage_sync(file, inode, page, offset, count, 0);
status = nfs_writepage_sync(ctx, inode, page, offset, count, 0);
if (status > 0) {
if (offset == 0 && status == PAGE_CACHE_SIZE)
SetPageUptodate(page);
......@@ -784,7 +792,7 @@ int nfs_updatepage(struct file *file, struct page *page,
* it out now.
*/
do {
req = nfs_update_request(file, inode, page, offset, count);
req = nfs_update_request(ctx, inode, page, offset, count);
status = (IS_ERR(req)) ? PTR_ERR(req) : 0;
if (status != -EBUSY)
break;
......@@ -860,16 +868,15 @@ static void nfs_write_rpcsetup(struct nfs_page *req,
* NB: take care not to mess about with data->commit et al. */
data->req = req;
data->inode = inode = req->wb_inode;
data->cred = req->wb_cred;
data->inode = inode = req->wb_context->dentry->d_inode;
data->cred = req->wb_context->cred;
data->args.fh = NFS_FH(inode);
data->args.offset = req_offset(req) + offset;
data->args.pgbase = req->wb_pgbase + offset;
data->args.pages = data->pagevec;
data->args.count = count;
data->args.lockowner = req->wb_lockowner;
data->args.state = req->wb_state;
data->args.context = req->wb_context;
data->res.fattr = &data->fattr;
data->res.count = count;
......@@ -1029,7 +1036,7 @@ nfs_flush_list(struct list_head *head, int wpages, int how)
while (!list_empty(head)) {
pages += nfs_coalesce_requests(head, &one_request, wpages);
req = nfs_list_entry(one_request.next);
error = nfs_flush_one(&one_request, req->wb_inode, how);
error = nfs_flush_one(&one_request, req->wb_context->dentry->d_inode, how);
if (error < 0)
break;
}
......@@ -1054,16 +1061,15 @@ static void nfs_writeback_done_partial(struct nfs_write_data *data, int status)
struct page *page = req->wb_page;
dprintk("NFS: write (%s/%Ld %d@%Ld)",
req->wb_inode->i_sb->s_id,
(long long)NFS_FILEID(req->wb_inode),
req->wb_context->dentry->d_inode->i_sb->s_id,
(long long)NFS_FILEID(req->wb_context->dentry->d_inode),
req->wb_bytes,
(long long)req_offset(req));
if (status < 0) {
ClearPageUptodate(page);
SetPageError(page);
if (req->wb_file)
req->wb_file->f_error = status;
req->wb_context->error = status;
dprintk(", error = %d\n", status);
} else {
#if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4)
......@@ -1104,16 +1110,15 @@ static void nfs_writeback_done_full(struct nfs_write_data *data, int status)
page = req->wb_page;
dprintk("NFS: write (%s/%Ld %d@%Ld)",
req->wb_inode->i_sb->s_id,
(long long)NFS_FILEID(req->wb_inode),
req->wb_context->dentry->d_inode->i_sb->s_id,
(long long)NFS_FILEID(req->wb_context->dentry->d_inode),
req->wb_bytes,
(long long)req_offset(req));
if (status < 0) {
ClearPageUptodate(page);
SetPageError(page);
if (req->wb_file)
req->wb_file->f_error = status;
req->wb_context->error = status;
end_page_writeback(page);
nfs_inode_remove_request(req);
dprintk(", error = %d\n", status);
......@@ -1232,7 +1237,7 @@ static void nfs_commit_rpcsetup(struct list_head *head,
list_splice_init(head, &data->pages);
first = nfs_list_entry(data->pages.next);
last = nfs_list_entry(data->pages.prev);
inode = first->wb_inode;
inode = first->wb_context->dentry->d_inode;
/*
* Determine the offset range of requests in the COMMIT call.
......@@ -1246,7 +1251,7 @@ static void nfs_commit_rpcsetup(struct list_head *head,
len = 0;
data->inode = inode;
data->cred = first->wb_cred;
data->cred = first->wb_context->cred;
data->args.fh = NFS_FH(data->inode);
data->args.offset = start;
......@@ -1313,13 +1318,12 @@ nfs_commit_done(struct rpc_task *task)
nfs_list_remove_request(req);
dprintk("NFS: commit (%s/%Ld %d@%Ld)",
req->wb_inode->i_sb->s_id,
(long long)NFS_FILEID(req->wb_inode),
req->wb_context->dentry->d_inode->i_sb->s_id,
(long long)NFS_FILEID(req->wb_context->dentry->d_inode),
req->wb_bytes,
(long long)req_offset(req));
if (task->tk_status < 0) {
if (req->wb_file)
req->wb_file->f_error = task->tk_status;
req->wb_context->error = task->tk_status;
nfs_inode_remove_request(req);
dprintk(", error = %d\n", task->tk_status);
goto next;
......@@ -1347,13 +1351,14 @@ nfs_commit_done(struct rpc_task *task)
int nfs_flush_inode(struct inode *inode, unsigned long idx_start,
unsigned int npages, int how)
{
struct nfs_inode *nfsi = NFS_I(inode);
LIST_HEAD(head);
int res,
error = 0;
spin_lock(&nfs_wreq_lock);
spin_lock(&nfsi->req_lock);
res = nfs_scan_dirty(inode, &head, idx_start, npages);
spin_unlock(&nfs_wreq_lock);
spin_unlock(&nfsi->req_lock);
if (res)
error = nfs_flush_list(&head, NFS_SERVER(inode)->wpages, how);
if (error < 0)
......@@ -1365,18 +1370,19 @@ int nfs_flush_inode(struct inode *inode, unsigned long idx_start,
int nfs_commit_inode(struct inode *inode, unsigned long idx_start,
unsigned int npages, int how)
{
struct nfs_inode *nfsi = NFS_I(inode);
LIST_HEAD(head);
int res,
error = 0;
spin_lock(&nfs_wreq_lock);
spin_lock(&nfsi->req_lock);
res = nfs_scan_commit(inode, &head, idx_start, npages);
if (res) {
res += nfs_scan_commit(inode, &head, 0, 0);
spin_unlock(&nfs_wreq_lock);
spin_unlock(&nfsi->req_lock);
error = nfs_commit_list(&head, how);
} else
spin_unlock(&nfs_wreq_lock);
spin_unlock(&nfsi->req_lock);
if (error < 0)
return error;
return res;
......
......@@ -8,6 +8,7 @@
#define _LINUX_NFS_H
#include <linux/sunrpc/msg_prot.h>
#include <linux/string.h>
#define NFS_PROGRAM 100003
#define NFS_PORT 2049
......@@ -138,6 +139,22 @@ struct nfs_fh {
unsigned char data[NFS_MAXFHSIZE];
};
/*
* Returns a zero iff the size and data fields match.
* Checks only "size" bytes in the data field.
*/
static inline int nfs_compare_fh(const struct nfs_fh *a, const struct nfs_fh *b)
{
return a->size != b->size || memcmp(a->data, b->data, a->size) != 0;
}
static inline void nfs_copy_fh(struct nfs_fh *target, const struct nfs_fh *source)
{
target->size = source->size;
memcpy(target->data, source->data, source->size);
}
/*
* This is really a general kernel constant, but since nothing like
* this is defined in the kernel headers, I have to do it here.
......
......@@ -18,6 +18,7 @@
#define NFS4_VERIFIER_SIZE 8
#define NFS4_FHSIZE 128
#define NFS4_MAXPATHLEN PATH_MAX
#define NFS4_MAXNAMLEN NAME_MAX
#define NFS4_ACCESS_READ 0x0001
......@@ -354,7 +355,7 @@ enum {
NFSPROC4_CLNT_COMMIT,
NFSPROC4_CLNT_OPEN,
NFSPROC4_CLNT_OPEN_CONFIRM,
NFSPROC4_CLNT_OPEN_RECLAIM,
NFSPROC4_CLNT_OPEN_NOATTR,
NFSPROC4_CLNT_OPEN_DOWNGRADE,
NFSPROC4_CLNT_CLOSE,
NFSPROC4_CLNT_SETATTR,
......@@ -372,12 +373,14 @@ enum {
NFSPROC4_CLNT_REMOVE,
NFSPROC4_CLNT_RENAME,
NFSPROC4_CLNT_LINK,
NFSPROC4_CLNT_SYMLINK,
NFSPROC4_CLNT_CREATE,
NFSPROC4_CLNT_PATHCONF,
NFSPROC4_CLNT_STATFS,
NFSPROC4_CLNT_READLINK,
NFSPROC4_CLNT_READDIR,
NFSPROC4_CLNT_SERVER_CAPS,
NFSPROC4_CLNT_DELEGRETURN,
};
#endif
......
......@@ -28,6 +28,7 @@
#include <linux/nfs3.h>
#include <linux/nfs4.h>
#include <linux/nfs_xdr.h>
#include <linux/rwsem.h>
#include <linux/workqueue.h>
/*
......@@ -75,15 +76,33 @@
#ifdef __KERNEL__
/*
* NFSv3 Access mode cache
* NFSv3/v4 Access mode cache entry
*/
struct nfs_access_cache {
struct nfs_access_entry {
unsigned long jiffies;
struct rpc_cred * cred;
int mask;
int err;
};
struct nfs4_state;
struct nfs_open_context {
atomic_t count;
struct dentry *dentry;
struct rpc_cred *cred;
struct nfs4_state *state;
fl_owner_t lockowner;
int mode;
int error;
struct list_head list;
wait_queue_head_t waitq;
};
/*
* NFSv4 delegation
*/
struct nfs_delegation;
/*
* nfs fs inode data in memory
*/
......@@ -137,7 +156,7 @@ struct nfs_inode {
*/
atomic_t data_updates;
struct nfs_access_cache cache_access;
struct nfs_access_entry cache_access;
/*
* This is the cookie verifier used for NFSv3 readdir
......@@ -148,6 +167,7 @@ struct nfs_inode {
/*
* This is the list of dirty unwritten pages.
*/
spinlock_t req_lock;
struct list_head dirty;
struct list_head commit;
struct radix_tree_root nfs_page_tree;
......@@ -156,14 +176,17 @@ struct nfs_inode {
ncommit,
npages;
/* Credentials for shared mmap */
struct rpc_cred *mm_cred;
/* Open contexts for shared mmap writes */
struct list_head open_files;
wait_queue_head_t nfs_i_wait;
#ifdef CONFIG_NFS_V4
/* NFSv4 state */
struct list_head open_states;
struct nfs_delegation *delegation;
int delegation_state;
struct rw_semaphore rwsem;
#endif /* CONFIG_NFS_V4*/
struct inode vfs_inode;
......@@ -268,9 +291,12 @@ extern struct inode *nfs_fhget(struct super_block *, struct nfs_fh *,
extern int nfs_refresh_inode(struct inode *, struct nfs_fattr *);
extern int nfs_getattr(struct vfsmount *, struct dentry *, struct kstat *);
extern int nfs_permission(struct inode *, int, struct nameidata *);
extern void nfs_set_mmcred(struct inode *, struct rpc_cred *);
extern int nfs_access_get_cached(struct inode *, struct rpc_cred *, struct nfs_access_entry *);
extern void nfs_access_add_cache(struct inode *, struct nfs_access_entry *);
extern int nfs_open(struct inode *, struct file *);
extern int nfs_release(struct inode *, struct file *);
extern int nfs_attribute_timeout(struct inode *inode);
extern int nfs_revalidate_inode(struct nfs_server *server, struct inode *inode);
extern int __nfs_revalidate_inode(struct nfs_server *, struct inode *);
extern int nfs_setattr(struct dentry *, struct iattr *);
extern void nfs_begin_attr_update(struct inode *);
......@@ -278,6 +304,12 @@ extern void nfs_end_attr_update(struct inode *);
extern void nfs_begin_data_update(struct inode *);
extern void nfs_end_data_update(struct inode *);
extern void nfs_end_data_update_defer(struct inode *);
extern struct nfs_open_context *alloc_nfs_open_context(struct dentry *dentry, struct rpc_cred *cred);
extern struct nfs_open_context *get_nfs_open_context(struct nfs_open_context *ctx);
extern void put_nfs_open_context(struct nfs_open_context *ctx);
extern void nfs_file_set_open_context(struct file *filp, struct nfs_open_context *ctx);
extern struct nfs_open_context *nfs_find_open_context(struct inode *inode, int mode);
extern void nfs_file_clear_open_context(struct file *filp);
/* linux/net/ipv4/ipconfig.c: trims ip addr off front of name, too. */
extern u32 root_nfs_parse_addr(char *name); /*__init*/
......@@ -289,16 +321,15 @@ extern struct inode_operations nfs_file_inode_operations;
extern struct file_operations nfs_file_operations;
extern struct address_space_operations nfs_file_aops;
static __inline__ struct rpc_cred *
nfs_file_cred(struct file *file)
static inline struct rpc_cred *nfs_file_cred(struct file *file)
{
struct rpc_cred *cred = NULL;
if (file)
cred = (struct rpc_cred *)file->private_data;
#ifdef RPC_DEBUG
BUG_ON(cred && cred->cr_magic != RPCAUTH_CRED_MAGIC);
#endif
return cred;
if (file != NULL) {
struct nfs_open_context *ctx;
ctx = (struct nfs_open_context*)file->private_data;
return ctx->cred;
}
return NULL;
}
/*
......@@ -418,28 +449,6 @@ extern int nfsroot_mount(struct sockaddr_in *, char *, struct nfs_fh *,
* inline functions
*/
static inline int nfs_attribute_timeout(struct inode *inode)
{
struct nfs_inode *nfsi = NFS_I(inode);
return time_after(jiffies, nfsi->read_cache_jiffies+nfsi->attrtimeo);
}
/**
* nfs_revalidate_inode - Revalidate the inode attributes
* @server - pointer to nfs_server struct
* @inode - pointer to inode struct
*
* Updates inode attribute information by retrieving the data from the server.
*/
static inline int nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
{
if (!(NFS_FLAGS(inode) & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA))
&& !nfs_attribute_timeout(inode))
return NFS_STALE(inode) ? -ESTALE : 0;
return __nfs_revalidate_inode(server, inode);
}
static inline loff_t
nfs_size_to_loff_t(__u64 size)
{
......@@ -507,8 +516,6 @@ struct idmap;
enum nfs4_client_state {
NFS4CLNT_OK = 0,
NFS4CLNT_NEW,
NFS4CLNT_SETUP_STATE,
};
/*
......@@ -520,7 +527,6 @@ struct nfs4_client {
u64 cl_clientid; /* constant */
nfs4_verifier cl_confirm;
unsigned long cl_state;
long cl_generation;
u32 cl_lockowner_id;
......@@ -530,6 +536,7 @@ struct nfs4_client {
*/
struct rw_semaphore cl_sem;
struct list_head cl_delegations;
struct list_head cl_state_owners;
struct list_head cl_unused;
int cl_nunused;
......@@ -573,12 +580,11 @@ struct nfs4_state_owner {
u32 so_id; /* 32-bit identifier, unique */
struct semaphore so_sema;
u32 so_seqid; /* protected by so_sema */
unsigned int so_flags; /* protected by so_sema */
atomic_t so_count;
long so_generation;
struct rpc_cred *so_cred; /* Associated cred */
struct list_head so_states;
struct list_head so_delegations;
};
/*
......@@ -596,7 +602,8 @@ struct nfs4_state_owner {
struct nfs4_lock_state {
struct list_head ls_locks; /* Other lock stateids */
fl_owner_t ls_owner; /* POSIX lock owner */
struct nfs4_state * ls_parent; /* Parent nfs4_state */
#define NFS_LOCK_INITIALIZED 1
int ls_flags;
u32 ls_seqid;
u32 ls_id;
nfs4_stateid ls_stateid;
......@@ -606,6 +613,7 @@ struct nfs4_lock_state {
/* bits for nfs4_state->flags */
enum {
LK_STATE_IN_USE,
NFS_DELEGATED_STATE,
};
struct nfs4_state {
......@@ -629,6 +637,11 @@ struct nfs4_state {
};
struct nfs4_exception {
long timeout;
int retry;
};
extern struct dentry_operations nfs4_dentry_operations;
extern struct inode_operations nfs4_dir_inode_operations;
......@@ -639,10 +652,12 @@ extern int nfs4_open_reclaim(struct nfs4_state_owner *, struct nfs4_state *);
extern int nfs4_proc_async_renew(struct nfs4_client *);
extern int nfs4_proc_renew(struct nfs4_client *);
extern int nfs4_do_close(struct inode *, struct nfs4_state *);
int nfs4_do_downgrade(struct inode *inode, struct nfs4_state *state, mode_t mode);
extern int nfs4_do_downgrade(struct inode *inode, struct nfs4_state *state, mode_t mode);
extern int nfs4_wait_clnt_recover(struct rpc_clnt *, struct nfs4_client *);
extern struct inode *nfs4_atomic_open(struct inode *, struct dentry *, struct nameidata *);
extern int nfs4_open_revalidate(struct inode *, struct dentry *, int);
extern int nfs4_handle_exception(struct nfs_server *, int, struct nfs4_exception *);
extern int nfs4_lock_reclaim(struct nfs4_state *state, struct file_lock *request);
/* nfs4renewd.c */
extern void nfs4_schedule_state_renewal(struct nfs4_client *);
......@@ -654,6 +669,8 @@ extern void init_nfsv4_state(struct nfs_server *);
extern void destroy_nfsv4_state(struct nfs_server *);
extern struct nfs4_client *nfs4_get_client(struct in_addr *);
extern void nfs4_put_client(struct nfs4_client *clp);
extern int nfs4_init_client(struct nfs4_client *clp);
extern struct nfs4_client *nfs4_find_client(struct in_addr *);
extern u32 nfs4_alloc_lockowner_id(struct nfs4_client *);
extern struct nfs4_state_owner * nfs4_get_state_owner(struct nfs_server *, struct rpc_cred *);
......@@ -663,14 +680,13 @@ extern void nfs4_put_open_state(struct nfs4_state *);
extern void nfs4_close_state(struct nfs4_state *, mode_t);
extern struct nfs4_state *nfs4_find_state(struct inode *, struct rpc_cred *, mode_t mode);
extern void nfs4_increment_seqid(int status, struct nfs4_state_owner *sp);
extern int nfs4_handle_error(struct nfs_server *, int);
extern void nfs4_schedule_state_recovery(struct nfs4_client *);
extern struct nfs4_lock_state *nfs4_find_lock_state(struct nfs4_state *state, fl_owner_t);
extern struct nfs4_lock_state *nfs4_alloc_lock_state(struct nfs4_state *state, fl_owner_t);
extern struct nfs4_lock_state *nfs4_get_lock_state(struct nfs4_state *state, fl_owner_t);
extern void nfs4_put_lock_state(struct nfs4_lock_state *state);
extern void nfs4_increment_lock_seqid(int status, struct nfs4_lock_state *ls);
extern void nfs4_notify_setlk(struct inode *, struct file_lock *, struct nfs4_lock_state *);
extern void nfs4_notify_unlck(struct inode *, struct file_lock *, struct nfs4_lock_state *);
extern void nfs4_notify_setlk(struct nfs4_state *, struct file_lock *, struct nfs4_lock_state *);
extern void nfs4_notify_unlck(struct nfs4_state *, struct file_lock *, struct nfs4_lock_state *);
extern void nfs4_copy_stateid(nfs4_stateid *, struct nfs4_state *, fl_owner_t);
......@@ -681,6 +697,7 @@ struct nfs4_mount_data;
#define destroy_nfsv4_state(server) do { } while (0)
#define nfs4_put_state_owner(inode, owner) do { } while (0)
#define nfs4_put_open_state(state) do { } while (0)
#define nfs4_close_state(a, b) do { } while (0)
#define nfs4_renewd_prepare_shutdown(server) do { } while (0)
#endif
......@@ -697,6 +714,7 @@ struct nfs4_mount_data;
#define NFSDBG_XDR 0x0020
#define NFSDBG_FILE 0x0040
#define NFSDBG_ROOT 0x0080
#define NFSDBG_CALLBACK 0x0100
#define NFSDBG_ALL 0xFFFF
#ifdef __KERNEL__
......
......@@ -18,6 +18,7 @@ struct nfs_server {
unsigned int rpages; /* read size (in pages) */
unsigned int wsize; /* write size */
unsigned int wpages; /* write size (in pages) */
unsigned int wtmult; /* server disk block size */
unsigned int dtsize; /* readdir size */
unsigned int bsize; /* server block size */
unsigned int acregmin; /* attr cache timeouts */
......
......@@ -29,19 +29,14 @@
struct nfs_page {
struct list_head wb_list, /* Defines state of page: */
*wb_list_head; /* read/write/commit */
struct file *wb_file;
fl_owner_t wb_lockowner;
struct inode *wb_inode;
struct rpc_cred *wb_cred;
struct nfs4_state *wb_state;
struct page *wb_page; /* page to read in/write out */
struct nfs_open_context *wb_context; /* File state context info */
atomic_t wb_complete; /* i/os we're waiting for */
wait_queue_head_t wb_wait; /* wait queue */
unsigned long wb_index; /* Offset >> PAGE_CACHE_SHIFT */
unsigned int wb_offset, /* Offset & ~PAGE_CACHE_MASK */
wb_pgbase, /* Start of page data */
wb_bytes, /* Length of request */
wb_count; /* reference count */
wb_bytes; /* Length of request */
atomic_t wb_count; /* reference count */
unsigned long wb_flags;
struct nfs_writeverf wb_verf; /* Commit cookie */
};
......@@ -50,9 +45,11 @@ struct nfs_page {
#define NFS_NEED_COMMIT(req) (test_bit(PG_NEED_COMMIT,&(req)->wb_flags))
#define NFS_NEED_RESCHED(req) (test_bit(PG_NEED_RESCHED,&(req)->wb_flags))
extern struct nfs_page *nfs_create_request(struct file *, struct inode *,
struct page *,
unsigned int, unsigned int);
extern struct nfs_page *nfs_create_request(struct nfs_open_context *ctx,
struct inode *inode,
struct page *page,
unsigned int offset,
unsigned int count);
extern void nfs_clear_request(struct nfs_page *req);
extern void nfs_release_request(struct nfs_page *req);
......@@ -64,8 +61,7 @@ extern int nfs_scan_list(struct list_head *, struct list_head *,
extern int nfs_coalesce_requests(struct list_head *, struct list_head *,
unsigned int);
extern int nfs_wait_on_request(struct nfs_page *);
extern spinlock_t nfs_wreq_lock;
extern void nfs_unlock_request(struct nfs_page *req);
/*
* Lock the page of an asynchronous request without incrementing the wb_count
......@@ -86,23 +82,10 @@ nfs_lock_request(struct nfs_page *req)
{
if (test_and_set_bit(PG_BUSY, &req->wb_flags))
return 0;
req->wb_count++;
atomic_inc(&req->wb_count);
return 1;
}
static inline void
nfs_unlock_request(struct nfs_page *req)
{
if (!NFS_WBACK_BUSY(req)) {
printk(KERN_ERR "NFS: Invalid unlock attempted\n");
BUG();
}
smp_mb__before_clear_bit();
clear_bit(PG_BUSY, &req->wb_flags);
smp_mb__after_clear_bit();
wake_up_all(&req->wb_wait);
nfs_release_request(req);
}
/**
* nfs_list_remove_request - Remove a request from its wb_list
......
......@@ -99,20 +99,21 @@ struct nfs4_change_info {
* Arguments to the open call.
*/
struct nfs_openargs {
struct nfs_fh * fh;
const struct nfs_fh * fh;
__u32 seqid;
__u32 share_access;
int open_flags;
__u64 clientid;
__u32 id;
__u32 opentype;
__u32 createmode;
union {
struct iattr * attrs; /* UNCHECKED, GUARDED */
nfs4_verifier verifier; /* EXCLUSIVE */
nfs4_stateid delegation; /* CLAIM_DELEGATE_CUR */
int delegation_type; /* CLAIM_PREVIOUS */
} u;
const struct qstr * name;
const struct nfs_server *server; /* Needed for ID mapping */
const u32 * bitmask;
__u32 claim;
};
struct nfs_openres {
......@@ -122,13 +123,17 @@ struct nfs_openres {
__u32 rflags;
struct nfs_fattr * f_attr;
const struct nfs_server *server;
int delegation_type;
nfs4_stateid delegation;
__u32 do_recall;
__u64 maxsize;
};
/*
* Arguments to the open_confirm call.
*/
struct nfs_open_confirmargs {
struct nfs_fh * fh;
const struct nfs_fh * fh;
nfs4_stateid stateid;
__u32 seqid;
};
......@@ -137,19 +142,6 @@ struct nfs_open_confirmres {
nfs4_stateid stateid;
};
/*
* Arguments to the open_reclaim call.
*/
struct nfs_open_reclaimargs {
struct nfs_fh * fh;
__u64 clientid;
__u32 seqid;
__u32 id;
__u32 share_access;
__u32 claim;
const __u32 * bitmask;
};
/*
* Arguments to the close call.
*/
......@@ -157,7 +149,7 @@ struct nfs_closeargs {
struct nfs_fh * fh;
nfs4_stateid stateid;
__u32 seqid;
__u32 share_access;
int open_flags;
};
struct nfs_closeres {
......@@ -224,6 +216,11 @@ struct nfs_lockres {
const struct nfs_server * server;
};
struct nfs4_delegreturnargs {
const struct nfs_fh *fhandle;
const nfs4_stateid *stateid;
};
/*
* Arguments to the read call.
*/
......@@ -235,8 +232,7 @@ struct nfs_lockres {
struct nfs_readargs {
struct nfs_fh * fh;
fl_owner_t lockowner;
struct nfs4_state * state;
struct nfs_open_context *context;
__u64 offset;
__u32 count;
unsigned int pgbase;
......@@ -259,8 +255,7 @@ struct nfs_readres {
struct nfs_writeargs {
struct nfs_fh * fh;
fl_owner_t lockowner;
struct nfs4_state * state;
struct nfs_open_context *context;
__u64 offset;
__u32 count;
enum nfs3_stable_how stable;
......@@ -597,13 +592,15 @@ struct nfs4_rename_res {
};
struct nfs4_setclientid {
nfs4_verifier sc_verifier; /* request */
char * sc_name; /* request */
const nfs4_verifier * sc_verifier; /* request */
unsigned int sc_name_len;
char sc_name[32]; /* request */
u32 sc_prog; /* request */
unsigned int sc_netid_len;
char sc_netid[4]; /* request */
unsigned int sc_uaddr_len;
char sc_uaddr[24]; /* request */
u32 sc_cb_ident; /* request */
struct nfs4_client * sc_state; /* response */
};
struct nfs4_statfs_arg {
......@@ -657,6 +654,8 @@ struct nfs_write_data {
void (*complete) (struct nfs_write_data *, int);
};
struct nfs_access_entry;
/*
* RPC procedure vector for NFSv2/NFSv3 demuxing
*/
......@@ -667,16 +666,17 @@ struct nfs_rpc_ops {
int (*getroot) (struct nfs_server *, struct nfs_fh *,
struct nfs_fsinfo *);
int (*getattr) (struct inode *, struct nfs_fattr *);
int (*getattr) (struct nfs_server *, struct nfs_fh *,
struct nfs_fattr *);
int (*setattr) (struct dentry *, struct nfs_fattr *,
struct iattr *);
int (*lookup) (struct inode *, struct qstr *,
struct nfs_fh *, struct nfs_fattr *);
int (*access) (struct inode *, struct rpc_cred *, int);
int (*access) (struct inode *, struct nfs_access_entry *);
int (*readlink)(struct inode *, struct page *);
int (*read) (struct nfs_read_data *, struct file *);
int (*write) (struct nfs_write_data *, struct file *);
int (*commit) (struct nfs_write_data *, struct file *);
int (*read) (struct nfs_read_data *);
int (*write) (struct nfs_write_data *);
int (*commit) (struct nfs_write_data *);
struct inode * (*create) (struct inode *, struct qstr *,
struct iattr *, int);
int (*remove) (struct inode *, struct qstr *);
......@@ -708,8 +708,6 @@ struct nfs_rpc_ops {
void (*commit_setup) (struct nfs_write_data *, int how);
int (*file_open) (struct inode *, struct file *);
int (*file_release) (struct inode *, struct file *);
void (*request_init)(struct nfs_page *, struct file *);
int (*request_compatible)(struct nfs_page *, struct file *, struct page *);
int (*lock)(struct file *, int, struct file_lock *);
};
......
......@@ -69,7 +69,6 @@ u32 g_verify_token_header(
struct xdr_netobj *mech,
int *body_size,
unsigned char **buf_in,
int tok_type,
int toksize);
u32 g_get_mech_oid(struct xdr_netobj *mech, struct xdr_netobj * in_buf);
......
/*
* linux/include/linux/sunrpc/gss_spkm3.h
*
* Copyright (c) 2000 The Regents of the University of Michigan.
* All rights reserved.
*
* Andy Adamson <andros@umich.edu>
*/
#include <linux/sunrpc/auth_gss.h>
#include <linux/sunrpc/gss_err.h>
#include <linux/sunrpc/gss_asn1.h>
struct spkm3_ctx {
struct xdr_netobj ctx_id; /* per message context id */
int qop; /* negotiated qop */
struct xdr_netobj mech_used;
unsigned int ret_flags ;
unsigned int req_flags ;
struct xdr_netobj share_key;
int conf_alg;
struct crypto_tfm* derived_conf_key;
int intg_alg;
struct crypto_tfm* derived_integ_key;
int keyestb_alg; /* alg used to get share_key */
int owf_alg; /* one way function */
};
/* from openssl/objects.h */
/* XXX need SEAL_ALG_NONE */
#define NID_md5 4
#define NID_dhKeyAgreement 28
#define NID_des_cbc 31
#define NID_sha1 64
#define NID_cast5_cbc 108
/* SPKM InnerContext Token types */
#define SPKM_ERROR_TOK 3
#define SPKM_MIC_TOK 4
#define SPKM_WRAP_TOK 5
#define SPKM_DEL_TOK 6
u32 spkm3_make_token(struct spkm3_ctx *ctx, int qop_req, struct xdr_buf * text, struct xdr_netobj * token, int toktype);
u32 spkm3_read_token(struct spkm3_ctx *ctx, struct xdr_netobj *read_token, struct xdr_buf *message_buffer, int *qop_state, int toktype);
#define CKSUMTYPE_RSA_MD5 0x0007
s32 make_checksum(s32 cksumtype, char *header, int hdrlen, struct xdr_buf *body,
struct xdr_netobj *cksum);
void asn1_bitstring_len(struct xdr_netobj *in, int *enclen, int *zerobits);
int decode_asn1_bitstring(struct xdr_netobj *out, char *in, int enclen,
int explen);
void spkm3_mic_header(unsigned char **hdrbuf, unsigned int *hdrlen,
unsigned char *ctxhdr, int elen, int zbit);
void spkm3_make_mic_token(unsigned char **tokp, int toklen,
struct xdr_netobj *mic_hdr,
struct xdr_netobj *md5cksum, int md5elen, int md5zbit);
u32 spkm3_verify_mic_token(unsigned char **tokp, int *mic_hdrlen,
unsigned char **cksum);
......@@ -113,6 +113,7 @@ typedef void (*rpc_action)(struct rpc_task *);
#define RPC_TASK_DYNAMIC 0x0080 /* task was kmalloc'ed */
#define RPC_TASK_KILLED 0x0100 /* task was killed */
#define RPC_TASK_SOFT 0x0200 /* Use soft timeouts */
#define RPC_TASK_NOINTR 0x0400 /* uninterruptible task */
#define RPC_IS_ASYNC(t) ((t)->tk_flags & RPC_TASK_ASYNC)
#define RPC_IS_SETUID(t) ((t)->tk_flags & RPC_TASK_SETUID)
......@@ -123,6 +124,7 @@ typedef void (*rpc_action)(struct rpc_task *);
#define RPC_IS_ACTIVATED(t) ((t)->tk_active)
#define RPC_DO_CALLBACK(t) ((t)->tk_callback != NULL)
#define RPC_IS_SOFT(t) ((t)->tk_flags & RPC_TASK_SOFT)
#define RPC_TASK_UNINTERRUPTIBLE(t) ((t)->tk_flags & RPC_TASK_NOINTR)
#define RPC_TASK_SLEEPING 0
#define RPC_TASK_RUNNING 1
......
......@@ -87,6 +87,14 @@ static inline u32 svc_getu32(struct kvec *iov)
iov->iov_len -= sizeof(u32);
return val;
}
static inline void svc_ungetu32(struct kvec *iov)
{
u32 *vp = (u32 *)iov->iov_base;
iov->iov_base = (void *)(vp - 1);
iov->iov_len += sizeof(*vp);
}
static inline void svc_putu32(struct kvec *iov, u32 val)
{
u32 *vp = iov->iov_base + iov->iov_len;
......@@ -243,6 +251,8 @@ struct svc_program {
char * pg_name; /* service name */
char * pg_class; /* class name: services sharing authentication */
struct svc_stat * pg_stats; /* rpc statistics */
/* Override authentication. NULL means use default */
int (*pg_authenticate)(struct svc_rqst *, u32 *);
};
/*
......
......@@ -12,3 +12,7 @@ obj-$(CONFIG_RPCSEC_GSS_KRB5) += rpcsec_gss_krb5.o
rpcsec_gss_krb5-objs := gss_krb5_mech.o gss_krb5_seal.o gss_krb5_unseal.o \
gss_krb5_seqnum.o
obj-$(CONFIG_RPCSEC_GSS_SPKM3) += rpcsec_gss_spkm3.o
rpcsec_gss_spkm3-objs := gss_spkm3_mech.o gss_spkm3_seal.o gss_spkm3_unseal.o \
gss_spkm3_token.o
......@@ -397,7 +397,7 @@ gss_upcall(struct rpc_clnt *clnt, struct rpc_task *task, struct rpc_cred *cred)
spin_unlock(&gss_auth->lock);
}
gss_release_msg(gss_msg);
dprintk("RPC: %4u gss_upcall for uid %u result %d", task->tk_pid,
dprintk("RPC: %4u gss_upcall for uid %u result %d\n", task->tk_pid,
uid, res);
return res;
out_sleep:
......
......@@ -179,7 +179,7 @@ EXPORT_SYMBOL(g_make_token_header);
*/
u32
g_verify_token_header(struct xdr_netobj *mech, int *body_size,
unsigned char **buf_in, int tok_type, int toksize)
unsigned char **buf_in, int toksize)
{
unsigned char *buf = *buf_in;
int seqsize;
......
......@@ -96,7 +96,7 @@ krb5_read_token(struct krb5_ctx *ctx,
dprintk("RPC: krb5_read_token\n");
if (g_verify_token_header(&ctx->mech_used, &bodysize, &ptr, toktype,
if (g_verify_token_header(&ctx->mech_used, &bodysize, &ptr,
read_token->len))
goto out;
......
/*
* linux/net/sunrpc/gss_spkm3_mech.c
*
* Copyright (c) 2003 The Regents of the University of Michigan.
* All rights reserved.
*
* Andy Adamson <andros@umich.edu>
* J. Bruce Fields <bfields@umich.edu>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/sunrpc/auth.h>
#include <linux/in.h>
#include <linux/sunrpc/svcauth_gss.h>
#include <linux/sunrpc/gss_spkm3.h>
#include <linux/sunrpc/xdr.h>
#include <linux/crypto.h>
#ifdef RPC_DEBUG
# define RPCDBG_FACILITY RPCDBG_AUTH
#endif
struct xdr_netobj gss_mech_spkm3_oid =
{7, "\053\006\001\005\005\001\003"};
static inline int
get_bytes(char **ptr, const char *end, void *res, int len)
{
char *p, *q;
p = *ptr;
q = p + len;
if (q > end || q < p)
return -1;
memcpy(res, p, len);
*ptr = q;
return 0;
}
static inline int
get_netobj(char **ptr, const char *end, struct xdr_netobj *res)
{
char *p, *q;
p = *ptr;
if (get_bytes(&p, end, &res->len, sizeof(res->len)))
return -1;
q = p + res->len;
if(res->len == 0)
goto out_nocopy;
if (q > end || q < p)
return -1;
if (!(res->data = kmalloc(res->len, GFP_KERNEL)))
return -1;
memcpy(res->data, p, res->len);
out_nocopy:
*ptr = q;
return 0;
}
static inline int
get_key(char **p, char *end, struct crypto_tfm **res, int *resalg)
{
struct xdr_netobj key = {
.len = 0,
.data = NULL,
};
int alg_mode,setkey = 0;
char *alg_name;
if (get_bytes(p, end, resalg, sizeof(int)))
goto out_err;
if ((get_netobj(p, end, &key)))
goto out_err;
switch (*resalg) {
case NID_des_cbc:
alg_name = "des";
alg_mode = CRYPTO_TFM_MODE_CBC;
setkey = 1;
break;
case NID_md5:
if (key.len == 0) {
dprintk("RPC: SPKM3 get_key: NID_md5 zero Key length\n");
}
alg_name = "md5";
alg_mode = 0;
setkey = 0;
break;
case NID_cast5_cbc:
dprintk("RPC: SPKM3 get_key: case cast5_cbc, UNSUPPORTED \n");
goto out_err;
break;
default:
dprintk("RPC: SPKM3 get_key: unsupported algorithm %d", *resalg);
goto out_err_free_key;
}
if (!(*res = crypto_alloc_tfm(alg_name, alg_mode)))
goto out_err_free_key;
if (setkey) {
if (crypto_cipher_setkey(*res, key.data, key.len))
goto out_err_free_tfm;
}
if(key.len > 0)
kfree(key.data);
return 0;
out_err_free_tfm:
crypto_free_tfm(*res);
out_err_free_key:
if(key.len > 0)
kfree(key.data);
out_err:
return -1;
}
static u32
gss_import_sec_context_spkm3(struct xdr_netobj *inbuf,
struct gss_ctx *ctx_id)
{
char *p = inbuf->data;
char *end = inbuf->data + inbuf->len;
struct spkm3_ctx *ctx;
if (!(ctx = kmalloc(sizeof(*ctx), GFP_KERNEL)))
goto out_err;
memset(ctx, 0, sizeof(*ctx));
if (get_netobj(&p, end, &ctx->ctx_id))
goto out_err_free_ctx;
if (get_bytes(&p, end, &ctx->qop, sizeof(ctx->qop)))
goto out_err_free_ctx_id;
if (get_netobj(&p, end, &ctx->mech_used))
goto out_err_free_mech;
if (get_bytes(&p, end, &ctx->ret_flags, sizeof(ctx->ret_flags)))
goto out_err_free_mech;
if (get_bytes(&p, end, &ctx->req_flags, sizeof(ctx->req_flags)))
goto out_err_free_mech;
if (get_netobj(&p, end, &ctx->share_key))
goto out_err_free_s_key;
if (get_key(&p, end, &ctx->derived_conf_key, &ctx->conf_alg)) {
dprintk("RPC: SPKM3 confidentiality key will be NULL\n");
}
if (get_key(&p, end, &ctx->derived_integ_key, &ctx->intg_alg)) {
dprintk("RPC: SPKM3 integrity key will be NULL\n");
}
if (get_bytes(&p, end, &ctx->owf_alg, sizeof(ctx->owf_alg)))
goto out_err_free_s_key;
if (get_bytes(&p, end, &ctx->owf_alg, sizeof(ctx->owf_alg)))
goto out_err_free_s_key;
if (p != end)
goto out_err_free_s_key;
ctx_id->internal_ctx_id = ctx;
dprintk("Succesfully imported new spkm context.\n");
return 0;
out_err_free_s_key:
kfree(ctx->share_key.data);
out_err_free_mech:
kfree(ctx->mech_used.data);
out_err_free_ctx_id:
kfree(ctx->ctx_id.data);
out_err_free_ctx:
kfree(ctx);
out_err:
return GSS_S_FAILURE;
}
void
gss_delete_sec_context_spkm3(void *internal_ctx) {
struct spkm3_ctx *sctx = internal_ctx;
if(sctx->derived_integ_key)
crypto_free_tfm(sctx->derived_integ_key);
if(sctx->derived_conf_key)
crypto_free_tfm(sctx->derived_conf_key);
if(sctx->share_key.data)
kfree(sctx->share_key.data);
if(sctx->mech_used.data)
kfree(sctx->mech_used.data);
kfree(sctx);
}
u32
gss_verify_mic_spkm3(struct gss_ctx *ctx,
struct xdr_buf *signbuf,
struct xdr_netobj *checksum,
u32 *qstate) {
u32 maj_stat = 0;
int qop_state = 0;
struct spkm3_ctx *sctx = ctx->internal_ctx_id;
dprintk("RPC: gss_verify_mic_spkm3 calling spkm3_read_token\n");
maj_stat = spkm3_read_token(sctx, checksum, signbuf, &qop_state,
SPKM_MIC_TOK);
if (!maj_stat && qop_state)
*qstate = qop_state;
dprintk("RPC: gss_verify_mic_spkm3 returning %d\n", maj_stat);
return maj_stat;
}
u32
gss_get_mic_spkm3(struct gss_ctx *ctx,
u32 qop,
struct xdr_buf *message_buffer,
struct xdr_netobj *message_token) {
u32 err = 0;
struct spkm3_ctx *sctx = ctx->internal_ctx_id;
dprintk("RPC: gss_get_mic_spkm3\n");
err = spkm3_make_token(sctx, qop, message_buffer,
message_token, SPKM_MIC_TOK);
return err;
}
static struct gss_api_ops gss_spkm3_ops = {
.gss_import_sec_context = gss_import_sec_context_spkm3,
.gss_get_mic = gss_get_mic_spkm3,
.gss_verify_mic = gss_verify_mic_spkm3,
.gss_delete_sec_context = gss_delete_sec_context_spkm3,
};
static struct pf_desc gss_spkm3_pfs[] = {
{RPC_AUTH_GSS_SPKM, 0, RPC_GSS_SVC_NONE, "spkm3"},
{RPC_AUTH_GSS_SPKMI, 0, RPC_GSS_SVC_INTEGRITY, "spkm3i"},
};
static struct gss_api_mech gss_spkm3_mech = {
.gm_name = "spkm3",
.gm_owner = THIS_MODULE,
.gm_ops = &gss_spkm3_ops,
.gm_pf_num = ARRAY_SIZE(gss_spkm3_pfs),
.gm_pfs = gss_spkm3_pfs,
};
static int __init init_spkm3_module(void)
{
int status;
status = gss_mech_register(&gss_spkm3_mech);
if (status)
printk("Failed to register spkm3 gss mechanism!\n");
return 0;
}
static void __exit cleanup_spkm3_module(void)
{
gss_mech_unregister(&gss_spkm3_mech);
}
MODULE_LICENSE("GPL");
module_init(init_spkm3_module);
module_exit(cleanup_spkm3_module);
/*
* linux/net/sunrpc/gss_spkm3_seal.c
*
* Copyright (c) 2003 The Regents of the University of Michigan.
* All rights reserved.
*
* Andy Adamson <andros@umich.edu>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/sunrpc/gss_spkm3.h>
#include <linux/random.h>
#include <linux/crypto.h>
#ifdef RPC_DEBUG
# define RPCDBG_FACILITY RPCDBG_AUTH
#endif
/*
* spkm3_make_token()
*
* Only SPKM_MIC_TOK with md5 intg-alg is supported
*/
u32
spkm3_make_token(struct spkm3_ctx *ctx, int qop_req,
struct xdr_buf * text, struct xdr_netobj * token,
int toktype)
{
s32 checksum_type;
char tokhdrbuf[25];
struct xdr_netobj md5cksum = {.len = 0, .data = NULL};
struct xdr_netobj mic_hdr = {.len = 0, .data = tokhdrbuf};
int tmsglen, tokenlen = 0;
unsigned char *ptr;
s32 now;
int ctxelen = 0, ctxzbit = 0;
int md5elen = 0, md5zbit = 0;
dprintk("RPC: spkm3_make_token\n");
now = jiffies;
if (qop_req != 0)
goto out_err;
if (ctx->ctx_id.len != 16) {
dprintk("RPC: spkm3_make_token BAD ctx_id.len %d\n",
ctx->ctx_id.len);
goto out_err;
}
switch (ctx->intg_alg) {
case NID_md5:
checksum_type = CKSUMTYPE_RSA_MD5;
break;
default:
dprintk("RPC: gss_spkm3_seal: ctx->signalg %d not"
" supported\n", ctx->intg_alg);
goto out_err;
}
/* XXX since we don't support WRAP, perhaps we don't care... */
if (ctx->conf_alg != NID_cast5_cbc) {
dprintk("RPC: gss_spkm3_seal: ctx->sealalg %d not supported\n",
ctx->conf_alg);
goto out_err;
}
if (toktype == SPKM_MIC_TOK) {
tmsglen = 0;
/* Calculate checksum over the mic-header */
asn1_bitstring_len(&ctx->ctx_id, &ctxelen, &ctxzbit);
spkm3_mic_header(&mic_hdr.data, &mic_hdr.len, ctx->ctx_id.data,
ctxelen, ctxzbit);
if (make_checksum(checksum_type, mic_hdr.data, mic_hdr.len,
text, &md5cksum))
goto out_err;
asn1_bitstring_len(&md5cksum, &md5elen, &md5zbit);
tokenlen = 10 + ctxelen + 1 + 2 + md5elen + 1;
/* Create token header using generic routines */
token->len = g_token_size(&ctx->mech_used, tokenlen + tmsglen);
ptr = token->data;
g_make_token_header(&ctx->mech_used, tokenlen + tmsglen, &ptr);
spkm3_make_mic_token(&ptr, tokenlen, &mic_hdr, &md5cksum, md5elen, md5zbit);
} else if (toktype == SPKM_WRAP_TOK) { /* Not Supported */
dprintk("RPC: gss_spkm3_seal: SPKM_WRAP_TOK not supported\n");
goto out_err;
}
kfree(md5cksum.data);
/* XXX need to implement sequence numbers, and ctx->expired */
return GSS_S_COMPLETE;
out_err:
if (md5cksum.data)
kfree(md5cksum.data);
token->data = 0;
token->len = 0;
return GSS_S_FAILURE;
}
/*
* linux/net/sunrpc/gss_spkm3_token.c
*
* Copyright (c) 2003 The Regents of the University of Michigan.
* All rights reserved.
*
* Andy Adamson <andros@umich.edu>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/sunrpc/gss_spkm3.h>
#include <linux/random.h>
#include <linux/crypto.h>
#ifdef RPC_DEBUG
# define RPCDBG_FACILITY RPCDBG_AUTH
#endif
/*
* asn1_bitstring_len()
*
* calculate the asn1 bitstring length of the xdr_netobject
*/
void
asn1_bitstring_len(struct xdr_netobj *in, int *enclen, int *zerobits)
{
int i, zbit = 0,elen = in->len;
char *ptr;
ptr = &in->data[in->len -1];
/* count trailing 0's */
for(i = in->len; i > 0; i--) {
if (*ptr == 0) {
ptr--;
elen--;
} else
break;
}
/* count number of 0 bits in final octet */
ptr = &in->data[elen - 1];
for(i = 0; i < 8; i++) {
short mask = 0x01;
if (!((mask << i) & *ptr))
zbit++;
else
break;
}
*enclen = elen;
*zerobits = zbit;
}
/*
* decode_asn1_bitstring()
*
* decode a bitstring into a buffer of the expected length.
* enclen = bit string length
* explen = expected length (define in rfc)
*/
int
decode_asn1_bitstring(struct xdr_netobj *out, char *in, int enclen, int explen)
{
if (!(out->data = kmalloc(explen,GFP_KERNEL)))
return 0;
out->len = explen;
memset(out->data, 0, explen);
memcpy(out->data, in, enclen);
return 1;
}
/*
* SPKMInnerContextToken choice SPKM_MIC asn1 token layout
*
* contextid is always 16 bytes plain data. max asn1 bitstring len = 17.
*
* tokenlen = pos[0] to end of token (max pos[45] with MD5 cksum)
*
* pos value
* ----------
* [0] a4 SPKM-MIC tag
* [1] ?? innertoken length (max 44)
*
*
* tok_hdr piece of checksum data starts here
*
* the maximum mic-header len = 9 + 17 = 26
* mic-header
* ----------
* [2] 30 SEQUENCE tag
* [3] ?? mic-header length: (max 23) = TokenID + ContextID
*
* TokenID - all fields constant and can be hardcoded
* -------
* [4] 02 Type 2
* [5] 02 Length 2
* [6][7] 01 01 TokenID (SPKM_MIC_TOK)
*
* ContextID - encoded length not constant, calculated
* ---------
* [8] 03 Type 3
* [9] ?? encoded length
* [10] ?? ctxzbit
* [11] contextid
*
* mic_header piece of checksum data ends here.
*
* int-cksum - encoded length not constant, calculated
* ---------
* [??] 03 Type 3
* [??] ?? encoded length
* [??] ?? md5zbit
* [??] int-cksum (NID_md5 = 16)
*
* maximum SPKM-MIC innercontext token length =
* 10 + encoded contextid_size(17 max) + 2 + encoded
* cksum_size (17 maxfor NID_md5) = 46
*/
/*
* spkm3_mic_header()
*
* Prepare the SPKM_MIC_TOK mic-header for check-sum calculation
* elen: 16 byte context id asn1 bitstring encoded length
*/
void
spkm3_mic_header(unsigned char **hdrbuf, unsigned int *hdrlen, unsigned char *ctxdata, int elen, int zbit)
{
char *hptr = *hdrbuf;
char *top = *hdrbuf;
*(u8 *)hptr++ = 0x30;
*(u8 *)hptr++ = elen + 7; /* on the wire header length */
/* tokenid */
*(u8 *)hptr++ = 0x02;
*(u8 *)hptr++ = 0x02;
*(u8 *)hptr++ = 0x01;
*(u8 *)hptr++ = 0x01;
/* coniextid */
*(u8 *)hptr++ = 0x03;
*(u8 *)hptr++ = elen + 1; /* add 1 to include zbit */
*(u8 *)hptr++ = zbit;
memcpy(hptr, ctxdata, elen);
hptr += elen;
*hdrlen = hptr - top;
}
/*
* spkm3_mic_innercontext_token()
*
* *tokp points to the beginning of the SPKM_MIC token described
* in rfc 2025, section 3.2.1:
*
*/
void
spkm3_make_mic_token(unsigned char **tokp, int toklen, struct xdr_netobj *mic_hdr, struct xdr_netobj *md5cksum, int md5elen, int md5zbit)
{
unsigned char *ict = *tokp;
*(u8 *)ict++ = 0xa4;
*(u8 *)ict++ = toklen - 2;
memcpy(ict, mic_hdr->data, mic_hdr->len);
ict += mic_hdr->len;
*(u8 *)ict++ = 0x03;
*(u8 *)ict++ = md5elen + 1; /* add 1 to include zbit */
*(u8 *)ict++ = md5zbit;
memcpy(ict, md5cksum->data, md5elen);
}
u32
spkm3_verify_mic_token(unsigned char **tokp, int *mic_hdrlen, unsigned char **cksum)
{
struct xdr_netobj spkm3_ctx_id = {.len =0, .data = NULL};
unsigned char *ptr = *tokp;
int ctxelen;
u32 ret = GSS_S_DEFECTIVE_TOKEN;
/* spkm3 innercontext token preamble */
if ((ptr[0] != 0xa4) || (ptr[2] != 0x30)) {
dprintk("RPC: BAD SPKM ictoken preamble\n");
goto out;
}
*mic_hdrlen = ptr[3];
/* token type */
if ((ptr[4] != 0x02) || (ptr[5] != 0x02)) {
dprintk("RPC: BAD asn1 SPKM3 token type\n");
goto out;
}
/* only support SPKM_MIC_TOK */
if((ptr[6] != 0x01) || (ptr[7] != 0x01)) {
dprintk("RPC: ERROR unsupported SPKM3 token \n");
goto out;
}
/* contextid */
if (ptr[8] != 0x03) {
dprintk("RPC: BAD SPKM3 asn1 context-id type\n");
goto out;
}
ctxelen = ptr[9];
if (ctxelen > 17) { /* length includes asn1 zbit octet */
dprintk("RPC: BAD SPKM3 contextid len %d\n", ctxelen);
goto out;
}
/* ignore ptr[10] */
if(!decode_asn1_bitstring(&spkm3_ctx_id, &ptr[11], ctxelen - 1, 16))
goto out;
/*
* in the current implementation: the optional int-alg is not present
* so the default int-alg (md5) is used the optional snd-seq field is
* also not present
*/
if (*mic_hdrlen != 6 + ctxelen) {
dprintk("RPC: BAD SPKM_ MIC_TOK header len %d: we only support default int-alg (should be absent) and do not support snd-seq\n", *mic_hdrlen);
goto out;
}
/* checksum */
*cksum = (&ptr[10] + ctxelen); /* ctxelen includes ptr[10] */
ret = GSS_S_COMPLETE;
out:
if (spkm3_ctx_id.data)
kfree(spkm3_ctx_id.data);
return ret;
}
/*
* linux/net/sunrpc/gss_spkm3_unseal.c
*
* Copyright (c) 2003 The Regents of the University of Michigan.
* All rights reserved.
*
* Andy Adamson <andros@umich.edu>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/sunrpc/gss_spkm3.h>
#include <linux/crypto.h>
#ifdef RPC_DEBUG
# define RPCDBG_FACILITY RPCDBG_AUTH
#endif
/*
* spkm3_read_token()
*
* only SPKM_MIC_TOK with md5 intg-alg is supported
*/
u32
spkm3_read_token(struct spkm3_ctx *ctx,
struct xdr_netobj *read_token, /* checksum */
struct xdr_buf *message_buffer, /* signbuf */
int *qop_state, int toktype)
{
s32 code;
struct xdr_netobj wire_cksum = {.len =0, .data = NULL};
struct xdr_netobj md5cksum = {.len = 0, .data = NULL};
unsigned char *ptr = (unsigned char *)read_token->data;
unsigned char *cksum;
int bodysize, md5elen;
int mic_hdrlen;
u32 ret = GSS_S_DEFECTIVE_TOKEN;
dprintk("RPC: spkm3_read_token read_token->len %d\n", read_token->len);
if (g_verify_token_header((struct xdr_netobj *) &ctx->mech_used,
&bodysize, &ptr, read_token->len))
goto out;
/* decode the token */
if (toktype == SPKM_MIC_TOK) {
if ((ret = spkm3_verify_mic_token(&ptr, &mic_hdrlen, &cksum)))
goto out;
if (*cksum++ != 0x03) {
dprintk("RPC: spkm3_read_token BAD checksum type\n");
goto out;
}
md5elen = *cksum++;
cksum++; /* move past the zbit */
if(!decode_asn1_bitstring(&wire_cksum, cksum, md5elen - 1, 16))
goto out;
/* HARD CODED FOR MD5 */
/* compute the checksum of the message.
* ptr + 2 = start of header piece of checksum
* mic_hdrlen + 2 = length of header piece of checksum
*/
ret = GSS_S_DEFECTIVE_TOKEN;
code = make_checksum(CKSUMTYPE_RSA_MD5, ptr + 2,
mic_hdrlen + 2,
message_buffer, &md5cksum);
if (code)
goto out;
dprintk("RPC: spkm3_read_token: digest wire_cksum.len %d:\n",
wire_cksum.len);
dprintk(" md5cksum.data\n");
print_hexl((u32 *) md5cksum.data, 16, 0);
dprintk(" cksum.data:\n");
print_hexl((u32 *) wire_cksum.data, wire_cksum.len, 0);
ret = GSS_S_BAD_SIG;
code = memcmp(md5cksum.data, wire_cksum.data, wire_cksum.len);
if (code)
goto out;
} else {
dprintk("RPC: BAD or UNSUPPORTED SPKM3 token type: %d\n",toktype);
goto out;
}
/* XXX: need to add expiration and sequencing */
ret = GSS_S_COMPLETE;
out:
if (md5cksum.data)
kfree(md5cksum.data);
if (wire_cksum.data)
kfree(wire_cksum.data);
return ret;
}
......@@ -196,7 +196,15 @@ rpc_clone_client(struct rpc_clnt *clnt)
memcpy(new, clnt, sizeof(*new));
atomic_set(&new->cl_count, 1);
atomic_set(&new->cl_users, 0);
atomic_inc(&new->cl_parent->cl_count);
new->cl_parent = clnt;
atomic_inc(&clnt->cl_count);
/* Duplicate portmapper */
rpc_init_wait_queue(&new->cl_pmap_default.pm_bindwait, "bindwait");
/* Turn off autobind on clones */
new->cl_autobind = 0;
new->cl_oneshot = 0;
new->cl_dead = 0;
rpc_init_rtt(&new->cl_rtt_default, clnt->cl_xprt->timeout.to_initval);
if (new->cl_auth)
atomic_inc(&new->cl_auth->au_count);
return new;
......@@ -335,7 +343,7 @@ void rpc_clnt_sigunmask(struct rpc_clnt *clnt, sigset_t *oldset)
*/
int rpc_call_sync(struct rpc_clnt *clnt, struct rpc_message *msg, int flags)
{
struct rpc_task my_task, *task = &my_task;
struct rpc_task *task;
sigset_t oldset;
int status;
......@@ -343,15 +351,15 @@ int rpc_call_sync(struct rpc_clnt *clnt, struct rpc_message *msg, int flags)
if (clnt->cl_dead)
return -EIO;
if (flags & RPC_TASK_ASYNC) {
printk("rpc_call_sync: Illegal flag combination for synchronous task\n");
flags &= ~RPC_TASK_ASYNC;
}
BUG_ON(flags & RPC_TASK_ASYNC);
rpc_clnt_sigmask(clnt, &oldset);
/* Create/initialize a new RPC task */
rpc_init_task(task, clnt, NULL, flags);
status = -ENOMEM;
task = rpc_new_task(clnt, NULL, flags);
if (task == NULL)
goto out;
rpc_call_setup(task, msg, 0);
/* Set up the call info struct and execute the task */
......@@ -362,6 +370,7 @@ int rpc_call_sync(struct rpc_clnt *clnt, struct rpc_message *msg, int flags)
rpc_release_task(task);
}
out:
rpc_clnt_sigunmask(clnt, &oldset);
return status;
......@@ -958,8 +967,12 @@ call_header(struct rpc_task *task)
static u32 *
call_verify(struct rpc_task *task)
{
u32 *p = task->tk_rqstp->rq_rcv_buf.head[0].iov_base, n;
struct kvec *iov = &task->tk_rqstp->rq_rcv_buf.head[0];
int len = task->tk_rqstp->rq_rcv_buf.len >> 2;
u32 *p = iov->iov_base, n;
if ((len -= 3) < 0)
goto garbage;
p += 1; /* skip XID */
if ((n = ntohl(*p++)) != RPC_REPLY) {
......@@ -969,9 +982,11 @@ call_verify(struct rpc_task *task)
if ((n = ntohl(*p++)) != RPC_MSG_ACCEPTED) {
int error = -EACCES;
if (--len < 0)
goto garbage;
if ((n = ntohl(*p++)) != RPC_AUTH_ERROR) {
printk(KERN_WARNING "call_verify: RPC call rejected: %x\n", n);
} else
} else if (--len < 0)
switch ((n = ntohl(*p++))) {
case RPC_AUTH_REJECTEDCRED:
case RPC_AUTH_REJECTEDVERF:
......@@ -1002,7 +1017,8 @@ call_verify(struct rpc_task *task)
default:
printk(KERN_WARNING "call_verify: unknown auth error: %x\n", n);
error = -EIO;
}
} else
goto garbage;
dprintk("RPC: %4d call_verify: call rejected %d\n",
task->tk_pid, n);
rpc_exit(task, error);
......@@ -1012,6 +1028,9 @@ call_verify(struct rpc_task *task)
printk(KERN_WARNING "call_verify: auth check failed\n");
goto garbage; /* bad verifier, retry */
}
len = p - (u32 *)iov->iov_base - 1;
if (len < 0)
goto garbage;
switch ((n = ntohl(*p++))) {
case RPC_SUCCESS:
return p;
......
......@@ -698,7 +698,7 @@ __rpc_execute(struct rpc_task *task)
if (current->pid == rpciod_pid)
printk(KERN_ERR "RPC: rpciod waiting on sync task!\n");
if (!task->tk_client->cl_intr) {
if (RPC_TASK_UNINTERRUPTIBLE(task)) {
__wait_event(task->tk_wait, !RPC_IS_SLEEPING(task));
} else {
__wait_event_interruptible(task->tk_wait, !RPC_IS_SLEEPING(task), status);
......@@ -884,6 +884,8 @@ void rpc_init_task(struct rpc_task *task, struct rpc_clnt *clnt, rpc_action call
atomic_inc(&clnt->cl_users);
if (clnt->cl_softrtry)
task->tk_flags |= RPC_TASK_SOFT;
if (!clnt->cl_intr)
task->tk_flags |= RPC_TASK_NOINTR;
}
#ifdef RPC_DEBUG
......
......@@ -89,6 +89,7 @@ EXPORT_SYMBOL(svc_makesock);
EXPORT_SYMBOL(svc_reserve);
EXPORT_SYMBOL(svc_auth_register);
EXPORT_SYMBOL(auth_domain_lookup);
EXPORT_SYMBOL(svc_authenticate);
/* RPC statistics */
#ifdef CONFIG_PROC_FS
......
......@@ -263,6 +263,7 @@ svc_process(struct svc_serv *serv, struct svc_rqst *rqstp)
u32 *statp;
u32 dir, prog, vers, proc,
auth_stat, rpc_stat;
int auth_res;
rpc_stat = rpc_success;
......@@ -304,12 +305,17 @@ svc_process(struct svc_serv *serv, struct svc_rqst *rqstp)
rqstp->rq_vers = vers = ntohl(svc_getu32(argv)); /* version number */
rqstp->rq_proc = proc = ntohl(svc_getu32(argv)); /* procedure number */
progp = serv->sv_program;
/*
* Decode auth data, and add verifier to reply buffer.
* We do this before anything else in order to get a decent
* auth verifier.
*/
switch (svc_authenticate(rqstp, &auth_stat)) {
if (progp->pg_authenticate != NULL)
auth_res = progp->pg_authenticate(rqstp, &auth_stat);
else
auth_res = svc_authenticate(rqstp, &auth_stat);
switch (auth_res) {
case SVC_OK:
break;
case SVC_GARBAGE:
......@@ -326,7 +332,6 @@ svc_process(struct svc_serv *serv, struct svc_rqst *rqstp)
goto sendit;
}
progp = serv->sv_program;
if (prog != progp->pg_prog)
goto err_bad_prog;
......
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