Commit 4de40457 authored by Trond Myklebust's avatar Trond Myklebust Committed by Linus Torvalds

[PATCH] NFSv4 cleanups

 - Move the encoding/decoding of the actual COMPOUND XDR header out of
   encode_compound()/decode_compound().

 - Make each NFSv4 operation 'decode_' routine also take care of
   decoding its own header, and checking it for correctness.
   Also allows us to get rid of the 'nfserr' parameter...
parent a0952771
......@@ -648,7 +648,7 @@ nfs4_call_compound(struct nfs4_compound *cp, struct rpc_cred *cred, int flags)
{
int status;
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_COMPOUND],
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_COMPOUND],
.rpc_argp = cp,
.rpc_resp = cp,
.rpc_cred = cred,
......@@ -1112,7 +1112,7 @@ nfs4_proc_unlink_setup(struct rpc_message *msg, struct dentry *dir, struct qstr
nfs4_setup_remove(cp, name, &up->cinfo);
nfs4_setup_getattr(cp, &up->attrs, bmres);
msg->rpc_proc = &nfs4_procedures[NFSPROC4_COMPOUND];
msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_COMPOUND];
msg->rpc_argp = cp;
msg->rpc_resp = cp;
return 0;
......@@ -1373,7 +1373,7 @@ nfs4_proc_read_setup(struct nfs_read_data *data, unsigned int count)
struct rpc_task *task = &data->task;
struct nfs4_compound *cp = &data->u.v4.compound;
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_COMPOUND],
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_COMPOUND],
.rpc_argp = cp,
.rpc_resp = cp,
.rpc_cred = data->cred,
......@@ -1417,7 +1417,7 @@ nfs4_proc_write_setup(struct nfs_write_data *data, unsigned int count, int how)
struct rpc_task *task = &data->task;
struct nfs4_compound *cp = &data->u.v4.compound;
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_COMPOUND],
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_COMPOUND],
.rpc_argp = cp,
.rpc_resp = cp,
.rpc_cred = data->cred,
......@@ -1468,7 +1468,7 @@ nfs4_proc_commit_setup(struct nfs_write_data *data, u64 start, u32 len, int how)
struct rpc_task *task = &data->task;
struct nfs4_compound *cp = &data->u.v4.compound;
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_COMPOUND],
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_COMPOUND],
.rpc_argp = cp,
.rpc_resp = cp,
.rpc_cred = data->cred,
......@@ -1523,7 +1523,7 @@ nfs4_proc_renew(struct nfs_server *server)
struct rpc_task *task;
struct nfs4_compound *cp;
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_COMPOUND],
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_COMPOUND],
};
rp = (struct renew_desc *) kmalloc(sizeof(*rp), GFP_KERNEL);
......
......@@ -83,6 +83,13 @@ static struct {
{ 0, NFNON },
};
struct compound_hdr {
int32_t status;
uint32_t nops;
uint32_t taglen;
char * tag;
};
/*
* START OF "GENERIC" ENCODE ROUTINES.
* These may look a little ugly since they are imported from a "generic"
......@@ -118,6 +125,20 @@ uint32_t *xdr_writemem(uint32_t *p, const void *ptr, int nbytes)
return p + tmp;
}
static int
encode_compound_hdr(struct xdr_stream *xdr, struct compound_hdr *hdr)
{
uint32_t *p;
dprintk("encode_compound: tag=%.*s\n", (int)hdr->taglen, hdr->tag);
RESERVE_SPACE(12+XDR_QUADLEN(hdr->taglen));
WRITE32(hdr->taglen);
WRITEMEM(hdr->tag, hdr->taglen);
WRITE32(NFS4_MINOR_VERSION);
WRITE32(hdr->nops);
return 0;
}
/*
* FIXME: The following dummy entries will be replaced once the userland
* upcall gets in...
......@@ -696,16 +717,14 @@ encode_write(struct xdr_stream *xdr, struct nfs4_write *write, struct rpc_rqst *
static int
encode_compound(struct xdr_stream *xdr, struct nfs4_compound *cp, struct rpc_rqst *req)
{
struct compound_hdr hdr = {
.taglen = cp->taglen,
.tag = cp->tag,
.nops = cp->req_nops,
};
int i, status = 0;
uint32_t *p;
dprintk("encode_compound: tag=%.*s\n", (int)cp->taglen, cp->tag);
RESERVE_SPACE(12 + cp->taglen);
WRITE32(cp->taglen);
WRITEMEM(cp->tag, cp->taglen);
WRITE32(NFS4_MINOR_VERSION);
WRITE32(cp->req_nops);
encode_compound_hdr(xdr, &hdr);
for (i = 0; i < cp->req_nops; i++) {
switch (cp->ops[i].opnum) {
......@@ -849,8 +868,8 @@ xdr_error: \
p = xdr_inline_decode(xdr, nbytes); \
if (!p) { \
printk(KERN_WARNING "%s: reply buffer overflowed in line %d.", \
__FUNCTION__, __LINE__); \
return -EIO; \
__FUNCTION__, __LINE__); \
return -EIO; \
} \
} while (0)
......@@ -876,6 +895,44 @@ decode_gid(char *p, uint32_t len, gid_t *gid)
return 0;
}
static int
decode_compound_hdr(struct xdr_stream *xdr, struct compound_hdr *hdr)
{
uint32_t *p;
READ_BUF(8);
READ32(hdr->status);
READ32(hdr->taglen);
READ_BUF(hdr->taglen + 4);
hdr->tag = (char *)p;
p += XDR_QUADLEN(hdr->taglen);
READ32(hdr->nops);
return 0;
}
static int
decode_op_hdr(struct xdr_stream *xdr, enum nfs_opnum4 expected)
{
uint32_t *p;
uint32_t opnum;
int32_t nfserr;
READ_BUF(8);
READ32(opnum);
if (opnum != expected) {
printk(KERN_NOTICE
"nfs4_decode_op_hdr: Server returned operation"
" %d but we issued a request for %d\n",
opnum, expected);
return -EIO;
}
READ32(nfserr);
if (nfserr != NFS_OK)
return -nfs_stat_to_errno(nfserr);
return 0;
}
static int
decode_change_info(struct xdr_stream *xdr, struct nfs4_change_info *cinfo)
{
......@@ -889,68 +946,71 @@ decode_change_info(struct xdr_stream *xdr, struct nfs4_change_info *cinfo)
}
static int
decode_access(struct xdr_stream *xdr, int nfserr, struct nfs4_access *access)
decode_access(struct xdr_stream *xdr, struct nfs4_access *access)
{
uint32_t *p;
uint32_t supp, acc;
int status;
if (!nfserr) {
READ_BUF(8);
READ32(supp);
READ32(acc);
if ((supp & ~access->ac_req_access) || (acc & ~supp)) {
printk(KERN_NOTICE "NFS: server returned bad bits in access call!\n");
return -EIO;
}
*access->ac_resp_supported = supp;
*access->ac_resp_access = acc;
status = decode_op_hdr(xdr, OP_ACCESS);
if (status)
return status;
READ_BUF(8);
READ32(supp);
READ32(acc);
if ((supp & ~access->ac_req_access) || (acc & ~supp)) {
printk(KERN_NOTICE "NFS: server returned bad bits in access call!\n");
return -EIO;
}
*access->ac_resp_supported = supp;
*access->ac_resp_access = acc;
return 0;
}
static int
decode_close(struct xdr_stream *xdr, int nfserr, struct nfs4_close *close)
decode_close(struct xdr_stream *xdr, struct nfs4_close *close)
{
uint32_t *p;
int status;
if (!nfserr) {
READ_BUF(sizeof(nfs4_stateid));
COPYMEM(close->cl_stateid, sizeof(nfs4_stateid));
}
status = decode_op_hdr(xdr, OP_CLOSE);
if (status)
return status;
READ_BUF(sizeof(nfs4_stateid));
COPYMEM(close->cl_stateid, sizeof(nfs4_stateid));
return 0;
}
static int
decode_commit(struct xdr_stream *xdr, int nfserr, struct nfs4_commit *commit)
decode_commit(struct xdr_stream *xdr, struct nfs4_commit *commit)
{
uint32_t *p;
int status;
if (!nfserr) {
READ_BUF(8);
COPYMEM(commit->co_verifier->verifier, 8);
}
status = decode_op_hdr(xdr, OP_COMMIT);
if (status)
return status;
READ_BUF(8);
COPYMEM(commit->co_verifier->verifier, 8);
return 0;
}
static int
decode_create(struct xdr_stream *xdr, int nfserr, struct nfs4_create *create)
decode_create(struct xdr_stream *xdr, struct nfs4_create *create)
{
uint32_t *p;
uint32_t bmlen;
int status;
if (!nfserr) {
if ((status = decode_change_info(xdr, create->cr_cinfo)))
goto out;
READ_BUF(4);
READ32(bmlen);
if (bmlen > 2)
goto xdr_error;
READ_BUF(bmlen << 2);
}
DECODE_TAIL;
status = decode_op_hdr(xdr, OP_CREATE);
if (status)
return status;
if ((status = decode_change_info(xdr, create->cr_cinfo)))
return status;
READ_BUF(4);
READ32(bmlen);
READ_BUF(bmlen << 2);
return 0;
}
extern uint32_t nfs4_fattr_bitmap[2];
......@@ -959,25 +1019,24 @@ extern uint32_t nfs4_fsstat_bitmap[2];
extern uint32_t nfs4_pathconf_bitmap[2];
static int
decode_getattr(struct xdr_stream *xdr, int nfserr, struct nfs4_getattr *getattr)
decode_getattr(struct xdr_stream *xdr, struct nfs4_getattr *getattr)
{
struct nfs_fattr *nfp = getattr->gt_attrs;
struct nfs_fattr *nfp = getattr->gt_attrs;
struct nfs_fsstat *fsstat = getattr->gt_fsstat;
struct nfs_fsinfo *fsinfo = getattr->gt_fsinfo;
struct nfs_pathconf *pathconf = getattr->gt_pathconf;
uint32_t attrlen, dummy32, bmlen,
bmval0 = 0,
bmval1 = 0,
len = 0;
uint32_t *p;
uint32_t bmlen;
uint32_t bmval0 = 0;
uint32_t bmval1 = 0;
uint32_t attrlen;
uint32_t dummy32;
uint32_t len = 0;
unsigned int type;
int fmode = 0;
int status;
if (nfserr)
goto success;
status = decode_op_hdr(xdr, OP_GETATTR);
if (status)
return status;
READ_BUF(4);
READ32(bmlen);
......@@ -1208,265 +1267,361 @@ decode_getattr(struct xdr_stream *xdr, int nfserr, struct nfs4_getattr *getattr)
if (len != attrlen)
goto xdr_error;
success:
DECODE_TAIL;
}
static int
decode_getfh(struct xdr_stream *xdr, int nfserr, struct nfs4_getfh *getfh)
decode_getfh(struct xdr_stream *xdr, struct nfs4_getfh *getfh)
{
struct nfs_fh *fh = getfh->gf_fhandle;
uint32_t *p;
uint32_t len;
int status;
status = decode_op_hdr(xdr, OP_GETFH);
if (status)
return status;
/* Zero handle first to allow comparisons */
memset(fh, 0, sizeof(*fh));
if (!nfserr) {
READ_BUF(4);
READ32(len);
if (len > NFS_MAXFHSIZE)
goto xdr_error;
fh->size = len;
READ_BUF(len);
COPYMEM(fh->data, len);
}
DECODE_TAIL;
READ_BUF(4);
READ32(len);
if (len > NFS_MAXFHSIZE)
return -EIO;
fh->size = len;
READ_BUF(len);
COPYMEM(fh->data, len);
return 0;
}
static int
decode_link(struct xdr_stream *xdr, int nfserr, struct nfs4_link *link)
decode_link(struct xdr_stream *xdr, struct nfs4_link *link)
{
int status = 0;
int status;
if (!nfserr)
status = decode_change_info(xdr, link->ln_cinfo);
return status;
status = decode_op_hdr(xdr, OP_LINK);
if (status)
return status;
return decode_change_info(xdr, link->ln_cinfo);
}
static int
decode_lookup(struct xdr_stream *xdr)
{
return decode_op_hdr(xdr, OP_LOOKUP);
}
static int
decode_open(struct xdr_stream *xdr, int nfserr, struct nfs4_open *open)
decode_open(struct xdr_stream *xdr, struct nfs4_open *open)
{
uint32_t *p;
uint32_t bmlen, delegation_type;
int status;
if (!nfserr) {
READ_BUF(sizeof(nfs4_stateid));
COPYMEM(open->op_stateid, sizeof(nfs4_stateid));
status = decode_op_hdr(xdr, OP_OPEN);
if (status)
return status;
READ_BUF(sizeof(nfs4_stateid));
COPYMEM(open->op_stateid, sizeof(nfs4_stateid));
decode_change_info(xdr, open->op_cinfo);
decode_change_info(xdr, open->op_cinfo);
READ_BUF(8);
READ32(*open->op_rflags);
READ32(bmlen);
if (bmlen > 10)
goto xdr_error;
READ_BUF(8);
READ32(*open->op_rflags);
READ32(bmlen);
if (bmlen > 10)
goto xdr_error;
READ_BUF((bmlen << 2) + 4);
p += bmlen;
READ32(delegation_type);
if (delegation_type != NFS4_OPEN_DELEGATE_NONE)
goto xdr_error;
}
READ_BUF((bmlen << 2) + 4);
p += bmlen;
READ32(delegation_type);
if (delegation_type != NFS4_OPEN_DELEGATE_NONE)
goto xdr_error;
DECODE_TAIL;
}
static int
decode_open_confirm(struct xdr_stream *xdr, int nfserr, struct nfs4_open_confirm *open_confirm)
decode_open_confirm(struct xdr_stream *xdr, struct nfs4_open_confirm *open_confirm)
{
uint32_t *p;
int status;
if (!nfserr) {
READ_BUF(sizeof(nfs4_stateid));
COPYMEM(open_confirm->oc_stateid, sizeof(nfs4_stateid));
}
status = decode_op_hdr(xdr, OP_OPEN_CONFIRM);
if (status)
return status;
READ_BUF(sizeof(nfs4_stateid));
COPYMEM(open_confirm->oc_stateid, sizeof(nfs4_stateid));
return 0;
}
static int
decode_read(struct xdr_stream *xdr, int nfserr, struct nfs4_read *read)
decode_putfh(struct xdr_stream *xdr)
{
return decode_op_hdr(xdr, OP_PUTFH);
}
static int
decode_putrootfh(struct xdr_stream *xdr)
{
uint32_t throwaway;
return decode_op_hdr(xdr, OP_PUTROOTFH);
}
static int
decode_read(struct xdr_stream *xdr, struct rpc_rqst *req, struct nfs4_read *read)
{
struct iovec *iov = req->rq_rvec;
uint32_t *p;
uint32_t count, eof, recvd, hdrlen;
int status;
if (!nfserr) {
READ_BUF(8);
if (read->rd_eof)
READ32(*read->rd_eof);
else
READ32(throwaway);
READ32(*read->rd_bytes_read);
if (*read->rd_bytes_read > read->rd_length)
goto xdr_error;
status = decode_op_hdr(xdr, OP_READ);
if (status)
return status;
READ_BUF(8);
READ32(eof);
READ32(count);
hdrlen = (u8 *) p - (u8 *) iov->iov_base;
if (iov->iov_len < hdrlen) {
printk(KERN_WARNING "NFS: READ reply header overflowed:"
"length %u > %Zu\n", hdrlen, iov->iov_len);
return -errno_NFSERR_IO;
} else if (iov->iov_len != hdrlen) {
dprintk("NFS: READ header is short. iovec will be shifted.\n");
xdr_shift_buf(&req->rq_rcv_buf, iov->iov_len - hdrlen);
}
DECODE_TAIL;
recvd = req->rq_received - hdrlen;
if (count > recvd) {
printk(KERN_WARNING "NFS: server cheating in read reply: "
"count %u > recvd %u\n", count, recvd);
count = recvd;
eof = 0;
}
if (read->rd_eof)
*read->rd_eof = eof;
*read->rd_bytes_read = count;
return 0;
}
static int
decode_readdir(struct xdr_stream *xdr, int nfserr, struct rpc_rqst *req, struct nfs4_readdir *readdir)
decode_readdir(struct xdr_stream *xdr, struct rpc_rqst *req, struct nfs4_readdir *readdir)
{
struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
struct page *page = *rcvbuf->pages;
unsigned int pglen = rcvbuf->page_len;
uint32_t *end, *entry, *p;
uint32_t len, attrlen, word;
int i;
if (!nfserr) {
READ_BUF(8);
COPYMEM(readdir->rd_resp_verifier, 8);
BUG_ON(pglen > PAGE_CACHE_SIZE);
p = (uint32_t *) kmap(page);
end = (uint32_t *) ((char *)p + pglen + readdir->rd_pgbase);
while (*p++) {
entry = p - 1;
if (p + 3 > end)
goto short_pkt;
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;
}
struct iovec *iov = rcvbuf->head;
unsigned int nr, pglen = rcvbuf->page_len;
uint32_t *end, *entry, *p;
uint32_t len, attrlen, word;
int i, hdrlen, recvd, status;
status = decode_op_hdr(xdr, OP_READDIR);
if (status)
return status;
READ_BUF(8);
COPYMEM(readdir->rd_resp_verifier, 8);
hdrlen = (char *) p - (char *) iov->iov_base;
if (iov->iov_len < hdrlen) {
printk(KERN_WARNING "NFS: READDIR reply header overflowed:"
"length %d > %Zu\n", hdrlen, iov->iov_len);
return -EIO;
} else if (iov->iov_len != hdrlen) {
dprintk("NFS: READDIR header is short. iovec will be shifted.\n");
xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen);
}
recvd = req->rq_received - hdrlen;
if (pglen > recvd)
pglen = recvd;
BUG_ON(pglen + readdir->rd_pgbase > PAGE_CACHE_SIZE);
p = (uint32_t *) kmap(page);
end = (uint32_t *) ((char *)p + pglen + readdir->rd_pgbase);
entry = p;
for (nr = 0; *p++; nr++) {
if (p + 3 > end)
goto short_pkt;
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(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;
}
p += XDR_QUADLEN(attrlen);
if (p + 1 > end)
goto short_pkt;
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;
}
kunmap(page);
p += XDR_QUADLEN(attrlen);
if (p + 1 > end)
goto short_pkt;
}
if (!nr && (entry[0] != 0 || entry[1] == 0))
goto short_pkt;
out:
kunmap(page);
return 0;
short_pkt:
printk(KERN_NOTICE "NFS: short packet in readdir reply!\n");
/* truncate listing */
kunmap(page);
entry[0] = entry[1] = 0;
return 0;
/* truncate listing ? */
if (!nr) {
printk(KERN_NOTICE "NFS: readdir reply truncated!\n");
entry[1] = 1;
}
goto out;
err_unmap:
kunmap(page);
return -errno_NFSERR_IO;
}
static int
decode_readlink(struct xdr_stream *xdr, int nfserr, struct rpc_rqst *req, struct nfs4_readlink *readlink)
decode_readlink(struct xdr_stream *xdr, struct rpc_rqst *req, struct nfs4_readlink *readlink)
{
struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
struct iovec *iov = rcvbuf->head;
uint32_t *strlen;
uint32_t len;
unsigned int hdrlen, len;
char *string;
int status;
if (!nfserr) {
/*
* The XDR encode routine has set things up so that
* the link text will be copied directly into the
* buffer. We just have to do overflow-checking,
* and and null-terminate the text (the VFS expects
* null-termination).
*/
strlen = (uint32_t *) kmap(rcvbuf->pages[0]);
len = ntohl(*strlen);
if (len > PAGE_CACHE_SIZE - 5) {
printk(KERN_WARNING "nfs: server returned giant symlink!\n");
kunmap(rcvbuf->pages[0]);
return -EIO;
}
*strlen = len;
string = (char *)(strlen + 1);
string[len] = '\0';
status = decode_op_hdr(xdr, OP_READLINK);
if (status)
return status;
hdrlen = (char *) xdr->p - (char *) iov->iov_base;
if (iov->iov_len > hdrlen) {
dprintk("NFS: READLINK header is short. iovec will be shifted.\n");
xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen);
}
/*
* The XDR encode routine has set things up so that
* the link text will be copied directly into the
* buffer. We just have to do overflow-checking,
* and and null-terminate the text (the VFS expects
* null-termination).
*/
strlen = (uint32_t *) kmap(rcvbuf->pages[0]);
len = ntohl(*strlen);
if (len > PAGE_CACHE_SIZE - 5) {
printk(KERN_WARNING "nfs: server returned giant symlink!\n");
kunmap(rcvbuf->pages[0]);
return -EIO;
}
*strlen = len;
string = (char *)(strlen + 1);
string[len] = '\0';
kunmap(rcvbuf->pages[0]);
return 0;
}
static int
decode_remove(struct xdr_stream *xdr, int nfserr, struct nfs4_remove *remove)
decode_restorefh(struct xdr_stream *xdr)
{
return decode_op_hdr(xdr, OP_RESTOREFH);
}
static int
decode_remove(struct xdr_stream *xdr, struct nfs4_remove *remove)
{
int status;
status = 0;
if (!nfserr)
status = decode_change_info(xdr, remove->rm_cinfo);
status = decode_op_hdr(xdr, OP_REMOVE);
if (status)
goto out;
status = decode_change_info(xdr, remove->rm_cinfo);
out:
return status;
}
static int
decode_rename(struct xdr_stream *xdr, int nfserr, struct nfs4_rename *rename)
decode_rename(struct xdr_stream *xdr, struct nfs4_rename *rename)
{
int status = 0;
int status;
if (!nfserr) {
if ((status = decode_change_info(xdr, rename->rn_src_cinfo)))
goto out;
if ((status = decode_change_info(xdr, rename->rn_dst_cinfo)))
goto out;
}
status = decode_op_hdr(xdr, OP_RENAME);
if (status)
goto out;
if ((status = decode_change_info(xdr, rename->rn_src_cinfo)))
goto out;
if ((status = decode_change_info(xdr, rename->rn_dst_cinfo)))
goto out;
out:
return status;
}
static int
decode_renew(struct xdr_stream *xdr)
{
return decode_op_hdr(xdr, OP_RENEW);
}
static int
decode_savefh(struct xdr_stream *xdr)
{
return decode_op_hdr(xdr, OP_SAVEFH);
}
static int
decode_setattr(struct xdr_stream *xdr)
{
uint32_t *p;
uint32_t bmlen;
uint32_t bmlen;
int status;
READ_BUF(4);
READ32(bmlen);
if (bmlen > 10)
goto xdr_error;
READ_BUF(bmlen << 2);
DECODE_TAIL;
status = decode_op_hdr(xdr, OP_SETATTR);
if (status)
return status;
READ_BUF(4);
READ32(bmlen);
READ_BUF(bmlen << 2);
return 0;
}
static int
decode_setclientid(struct xdr_stream *xdr, int nfserr, struct nfs4_setclientid *setclientid)
decode_setclientid(struct xdr_stream *xdr, struct nfs4_setclientid *setclientid)
{
uint32_t *p;
uint32_t opnum;
int32_t nfserr;
if (!nfserr) {
READ_BUF(8);
READ32(opnum);
if (opnum != OP_SETCLIENTID) {
printk(KERN_NOTICE
"nfs4_decode_setclientid: Server returned operation"
" %d\n", opnum);
return -EIO;
}
READ32(nfserr);
if (nfserr == NFS_OK) {
READ_BUF(8 + sizeof(nfs4_verifier));
READ64(setclientid->sc_state->cl_clientid);
COPYMEM(setclientid->sc_state->cl_confirm, sizeof(nfs4_verifier));
}
else if (nfserr == NFSERR_CLID_INUSE) {
} else if (nfserr == NFSERR_CLID_INUSE) {
uint32_t len;
/* skip netid string */
......@@ -1478,156 +1633,146 @@ decode_setclientid(struct xdr_stream *xdr, int nfserr, struct nfs4_setclientid *
READ_BUF(4);
READ32(len);
READ_BUF(len);
}
return -EEXIST;
} else
return -nfs_stat_to_errno(nfserr);
return 0;
}
static int
decode_write(struct xdr_stream *xdr, int nfserr, struct nfs4_write *write)
decode_setclientid_confirm(struct xdr_stream *xdr)
{
return decode_op_hdr(xdr, OP_SETCLIENTID_CONFIRM);
}
static int
decode_write(struct xdr_stream *xdr, struct nfs4_write *write)
{
uint32_t *p;
int status;
if (!nfserr) {
READ_BUF(16);
READ32(*write->wr_bytes_written);
if (*write->wr_bytes_written > write->wr_len)
goto xdr_error;
READ32(write->wr_verf->committed);
COPYMEM(write->wr_verf->verifier, 8);
}
status = decode_op_hdr(xdr, OP_WRITE);
if (status)
return status;
DECODE_TAIL;
READ_BUF(16);
READ32(*write->wr_bytes_written);
if (*write->wr_bytes_written > write->wr_len)
return -EIO;
READ32(write->wr_verf->committed);
COPYMEM(write->wr_verf->verifier, 8);
return 0;
}
/* FIXME: this sucks */
static int
decode_compound(struct xdr_stream *xdr, struct nfs4_compound *cp, struct rpc_rqst *req)
{
uint32_t *p;
uint32_t taglen;
uint32_t opnum, nfserr;
struct compound_hdr hdr;
struct nfs4_op *op;
int status;
READ_BUF(8);
READ32(cp->toplevel_status);
READ32(taglen);
status = decode_compound_hdr(xdr, &hdr);
if (status)
goto out;
cp->toplevel_status = hdr.status;
/*
* We need this if our zero-copy I/O is going to work. Rumor has
* it that the spec will soon mandate it...
*/
if (taglen != cp->taglen)
if (hdr.taglen != cp->taglen)
dprintk("nfs4: non-conforming server returns tag length mismatch!\n");
READ_BUF(taglen + 4);
p += XDR_QUADLEN(taglen);
READ32(cp->resp_nops);
if (cp->resp_nops > cp->req_nops) {
cp->resp_nops = hdr.nops;
if (hdr.nops > cp->req_nops) {
dprintk("nfs4: resp_nops > req_nops!\n");
goto xdr_error;
}
for (cp->nops = 0; cp->nops < cp->resp_nops; cp->nops++) {
READ_BUF(8);
READ32(opnum);
if (opnum != cp->ops[cp->nops].opnum) {
dprintk("nfs4: operation mismatch!\n");
goto xdr_error;
}
READ32(nfserr);
if (cp->nops == cp->resp_nops - 1) {
if (nfserr != cp->toplevel_status) {
dprintk("nfs4: status mismatch!\n");
goto xdr_error;
}
}
else if (nfserr) {
dprintk("nfs4: intermediate status nonzero!\n");
goto xdr_error;
}
cp->ops[cp->nops].nfserr = nfserr;
switch (opnum) {
op = &cp->ops[0];
for (cp->nops = 0; cp->nops < cp->resp_nops; cp->nops++, op++) {
switch (op->opnum) {
case OP_ACCESS:
status = decode_access(xdr, nfserr, &cp->ops[cp->nops].u.access);
status = decode_access(xdr, &op->u.access);
break;
case OP_CLOSE:
status = decode_close(xdr, nfserr, &cp->ops[cp->nops].u.close);
status = decode_close(xdr, &op->u.close);
break;
case OP_COMMIT:
status = decode_commit(xdr, nfserr, &cp->ops[cp->nops].u.commit);
status = decode_commit(xdr, &op->u.commit);
break;
case OP_CREATE:
status = decode_create(xdr, nfserr, &cp->ops[cp->nops].u.create);
status = decode_create(xdr, &op->u.create);
break;
case OP_GETATTR:
status = decode_getattr(xdr, nfserr, &cp->ops[cp->nops].u.getattr);
status = decode_getattr(xdr, &op->u.getattr);
break;
case OP_GETFH:
status = decode_getfh(xdr, nfserr, &cp->ops[cp->nops].u.getfh);
status = decode_getfh(xdr, &op->u.getfh);
break;
case OP_LINK:
status = decode_link(xdr, nfserr, &cp->ops[cp->nops].u.link);
status = decode_link(xdr, &op->u.link);
break;
case OP_LOOKUP:
status = 0;
status = decode_lookup(xdr);
break;
case OP_OPEN:
status = decode_open(xdr, nfserr, &cp->ops[cp->nops].u.open);
status = decode_open(xdr, &op->u.open);
break;
case OP_OPEN_CONFIRM:
status = decode_open_confirm(xdr, nfserr, &cp->ops[cp->nops].u.open_confirm);
status = decode_open_confirm(xdr, &op->u.open_confirm);
break;
case OP_PUTFH:
status = 0;
status = decode_putfh(xdr);
break;
case OP_PUTROOTFH:
status = 0;
status = decode_putrootfh(xdr);
break;
case OP_READ:
status = decode_read(xdr, nfserr, &cp->ops[cp->nops].u.read);
status = decode_read(xdr, req, &op->u.read);
break;
case OP_READDIR:
status = decode_readdir(xdr, nfserr, req, &cp->ops[cp->nops].u.readdir);
status = decode_readdir(xdr, req, &op->u.readdir);
break;
case OP_READLINK:
status = decode_readlink(xdr, nfserr, req, &cp->ops[cp->nops].u.readlink);
status = decode_readlink(xdr, req, &op->u.readlink);
break;
case OP_RESTOREFH:
status = 0;
status = decode_restorefh(xdr);
break;
case OP_REMOVE:
status = decode_remove(xdr, nfserr, &cp->ops[cp->nops].u.remove);
status = decode_remove(xdr, &op->u.remove);
break;
case OP_RENAME:
status = decode_rename(xdr, nfserr, &cp->ops[cp->nops].u.rename);
status = decode_rename(xdr, &op->u.rename);
break;
case OP_RENEW:
status = 0;
status = decode_renew(xdr);
break;
case OP_SAVEFH:
status = 0;
status = decode_savefh(xdr);
break;
case OP_SETATTR:
status = decode_setattr(xdr);
break;
case OP_SETCLIENTID:
status = decode_setclientid(xdr, nfserr, &cp->ops[cp->nops].u.setclientid);
status = decode_setclientid(xdr, &op->u.setclientid);
break;
case OP_SETCLIENTID_CONFIRM:
status = 0;
status = decode_setclientid_confirm(xdr);
break;
case OP_WRITE:
status = decode_write(xdr, nfserr, &cp->ops[cp->nops].u.write);
status = decode_write(xdr, &op->u.write);
break;
default:
BUG();
return -EIO;
}
if (status)
goto xdr_error;
break;
}
DECODE_TAIL;
......@@ -1700,15 +1845,15 @@ nfs4_decode_dirent(uint32_t *p, struct nfs_entry *entry, int plus)
#endif
#define PROC(proc, argtype, restype) \
[NFSPROC4_##proc] = { \
.p_proc = NFSPROC4_##proc, \
[NFSPROC4_CLNT_##proc] = { \
.p_proc = NFSPROC4_COMPOUND, \
.p_encode = (kxdrproc_t) nfs4_xdr_##argtype, \
.p_decode = (kxdrproc_t) nfs4_xdr_##restype, \
.p_bufsiz = MAX(NFS4_##argtype##_sz,NFS4_##restype##_sz) << 2, \
}
struct rpc_procinfo nfs4_procedures[] = {
PROC(COMPOUND, enc_compound, dec_compound)
PROC(COMPOUND, enc_compound, dec_compound),
};
struct rpc_version nfs_version4 = {
......
......@@ -196,6 +196,16 @@ enum open_delegation_type4 {
#define NFS4_MINOR_VERSION 0
#define NFS4_DEBUG 1
#ifdef __KERNEL__
/* Index of predefined Linux client operations */
enum {
NFSPROC4_CLNT_NULL = 0, /* Unused */
NFSPROC4_CLNT_COMPOUND, /* Soon to be unused */
};
#endif
#endif
/*
......
......@@ -488,7 +488,6 @@ struct nfs4_write {
struct nfs4_op {
u32 opnum;
u32 nfserr;
union {
struct nfs4_access access;
struct nfs4_close close;
......
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