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