Commit 89fc0a31 authored by Neil Brown's avatar Neil Brown Committed by Linus Torvalds

[PATCH] kNFSd - 1 of 2 - Change NFSv4 xdr decoding to cope with separate pages.

Now that nfsd uses a list of pages for requests instead of
one large buffer, NFSv4 need to know about this.

The most interesting part of this is that it is possible
that section of a request, like a path name, could span
two pages, so we need to be able to kmalloc as little bit
of space to copy them into, and make sure they get
freed later.
parent 586a5a35
......@@ -481,7 +481,8 @@ nfsd4_write(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_writ
*p++ = nfssvc_boot.tv_usec;
return nfsd_write(rqstp, current_fh, write->wr_offset,
write->wr_buf, write->wr_buflen, &write->wr_how_written);
write->wr_vec, write->wr_vlen, write->wr_buflen,
&write->wr_how_written);
}
/* This routine never returns NFS_OK! If there are no other errors, it
......@@ -700,6 +701,16 @@ nfsd4_proc_compound(struct svc_rqst *rqstp,
kfree(args->ops);
args->ops = args->iops;
}
if (args->tmpp) {
kfree(args->tmpp);
args->tmpp = NULL;
}
while (args->to_free) {
struct tmpbuf *tb = args->to_free;
args->to_free = tb->next;
kfree(tb->buf);
kfree(tb);
}
fh_put(&current_fh);
fh_put(&save_fh);
return status;
......
......@@ -217,20 +217,92 @@ xdr_error: \
x = (char *)p; \
p += XDR_QUADLEN(nbytes); \
} while (0)
#define SAVEMEM(x,nbytes) do { \
if (!(x = (p==argp->tmp || p == argp->tmpp) ? \
savemem(argp, p, nbytes) : \
(char *)p)) { \
printk(KERN_NOTICE "xdr error! (%s:%d)\n", __FILE__, __LINE__); \
goto xdr_error; \
} \
p += XDR_QUADLEN(nbytes); \
} while (0)
#define COPYMEM(x,nbytes) do { \
memcpy((x), p, nbytes); \
p += XDR_QUADLEN(nbytes); \
} while (0)
#define READ_BUF(nbytes) do { \
if (nbytes > (u32)((char *)argp->end - (char *)argp->p)) { \
if (nbytes <= (u32)((char *)argp->end - (char *)argp->p)) { \
p = argp->p; \
argp->p += XDR_QUADLEN(nbytes); \
} else if (!(p = read_buf(argp, nbytes))) { \
printk(KERN_NOTICE "xdr error! (%s:%d)\n", __FILE__, __LINE__); \
goto xdr_error; \
} \
p = argp->p; \
argp->p += XDR_QUADLEN(nbytes); \
} while (0)
u32 *read_buf(struct nfsd4_compoundargs *argp, int nbytes)
{
/* We want more bytes than seem to be available.
* Maybe we need a new page, may wehave just run out
*/
int avail = (char*)argp->end - (char*)argp->p;
u32 *p;
if (avail + argp->pagelen < nbytes)
return NULL;
if (avail + PAGE_SIZE > nbytes) /* need more than a page !! */
return NULL;
/* ok, we can do it with the tail plus the next page */
if (nbytes <= sizeof(argp->tmp))
p = argp->tmp;
else {
if (argp->tmpp)
kfree(argp->tmpp);
p = argp->tmpp = kmalloc(nbytes, GFP_KERNEL);
if (!p)
return NULL;
}
memcpy(p, argp->p, avail);
/* step to next page */
argp->p = page_address(argp->pagelist[0]);
argp->pagelist++;
if (argp->pagelen < PAGE_SIZE) {
argp->end = p + (argp->pagelen>>2);
argp->pagelen = 0;
} else {
argp->end = p + (PAGE_SIZE>>2);
argp->pagelen -= PAGE_SIZE;
}
memcpy(((char*)p)+avail, argp->p, (nbytes - avail));
argp->p += XDR_QUADLEN(nbytes - avail);
return p;
}
char *savemem(struct nfsd4_compoundargs *argp, u32 *p, int nbytes)
{
struct tmpbuf *tb;
if (p == argp->tmp) {
p = kmalloc(nbytes, GFP_KERNEL);
if (!p) return NULL;
memcpy(p, argp->tmp, nbytes);
} else {
if (p != argp->tmpp)
BUG();
argp->tmpp = NULL;
}
tb = kmalloc(sizeof(*tb), GFP_KERNEL);
if (!tb) {
kfree(p);
return NULL;
}
tb->buf = p;
tb->next = argp->to_free;
argp->to_free = tb;
return (char*)p;
}
static int
nfsd4_decode_bitmap(struct nfsd4_compoundargs *argp, u32 *bmval)
{
......@@ -442,7 +514,7 @@ nfsd4_decode_create(struct nfsd4_compoundargs *argp, struct nfsd4_create *create
READ_BUF(4);
READ32(create->cr_linklen);
READ_BUF(create->cr_linklen);
READMEM(create->cr_linkname, create->cr_linklen);
SAVEMEM(create->cr_linkname, create->cr_linklen);
if (check_utf8(create->cr_linkname, create->cr_linklen))
return nfserr_inval;
break;
......@@ -463,7 +535,7 @@ nfsd4_decode_create(struct nfsd4_compoundargs *argp, struct nfsd4_create *create
READ_BUF(4);
READ32(create->cr_namelen);
READ_BUF(create->cr_namelen);
READMEM(create->cr_name, create->cr_namelen);
SAVEMEM(create->cr_name, create->cr_namelen);
if ((status = check_filename(create->cr_name, create->cr_namelen, nfserr_inval)))
return status;
......@@ -487,7 +559,7 @@ nfsd4_decode_link(struct nfsd4_compoundargs *argp, struct nfsd4_link *link)
READ_BUF(4);
READ32(link->li_namelen);
READ_BUF(link->li_namelen);
READMEM(link->li_name, link->li_namelen);
SAVEMEM(link->li_name, link->li_namelen);
if ((status = check_filename(link->li_name, link->li_namelen, nfserr_inval)))
return status;
......@@ -502,7 +574,7 @@ nfsd4_decode_lookup(struct nfsd4_compoundargs *argp, struct nfsd4_lookup *lookup
READ_BUF(4);
READ32(lookup->lo_len);
READ_BUF(lookup->lo_len);
READMEM(lookup->lo_name, lookup->lo_len);
SAVEMEM(lookup->lo_name, lookup->lo_len);
if ((status = check_filename(lookup->lo_name, lookup->lo_len, nfserr_noent)))
return status;
......@@ -527,7 +599,7 @@ nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open)
/* owner, open_flag */
READ_BUF(open->op_ownerlen + 4);
READMEM(open->op_owner, open->op_ownerlen);
SAVEMEM(open->op_owner, open->op_ownerlen);
READ32(open->op_create);
switch (open->op_create) {
case NFS4_OPEN_NOCREATE:
......@@ -562,7 +634,7 @@ nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open)
READ_BUF(4);
READ32(open->op_namelen);
READ_BUF(open->op_namelen);
READMEM(open->op_name, open->op_namelen);
SAVEMEM(open->op_name, open->op_namelen);
if ((status = check_filename(open->op_name, open->op_namelen, nfserr_inval)))
return status;
break;
......@@ -575,7 +647,7 @@ nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open)
COPYMEM(&open->op_delegate_stateid, sizeof(delegation_stateid_t));
READ32(open->op_namelen);
READ_BUF(open->op_namelen);
READMEM(open->op_name, open->op_namelen);
SAVEMEM(open->op_name, open->op_namelen);
if ((status = check_filename(open->op_name, open->op_namelen, nfserr_inval)))
return status;
break;
......@@ -596,7 +668,7 @@ nfsd4_decode_putfh(struct nfsd4_compoundargs *argp, struct nfsd4_putfh *putfh)
if (putfh->pf_fhlen > NFS4_FHSIZE)
goto xdr_error;
READ_BUF(putfh->pf_fhlen);
READMEM(putfh->pf_fhval, putfh->pf_fhlen);
SAVEMEM(putfh->pf_fhval, putfh->pf_fhlen);
DECODE_TAIL;
}
......@@ -639,7 +711,7 @@ nfsd4_decode_remove(struct nfsd4_compoundargs *argp, struct nfsd4_remove *remove
READ_BUF(4);
READ32(remove->rm_namelen);
READ_BUF(remove->rm_namelen);
READMEM(remove->rm_name, remove->rm_namelen);
SAVEMEM(remove->rm_name, remove->rm_namelen);
if ((status = check_filename(remove->rm_name, remove->rm_namelen, nfserr_noent)))
return status;
......@@ -654,10 +726,10 @@ nfsd4_decode_rename(struct nfsd4_compoundargs *argp, struct nfsd4_rename *rename
READ_BUF(4);
READ32(rename->rn_snamelen);
READ_BUF(rename->rn_snamelen + 4);
READMEM(rename->rn_sname, rename->rn_snamelen);
SAVEMEM(rename->rn_sname, rename->rn_snamelen);
READ32(rename->rn_tnamelen);
READ_BUF(rename->rn_tnamelen);
READMEM(rename->rn_tname, rename->rn_tnamelen);
SAVEMEM(rename->rn_tname, rename->rn_tnamelen);
if ((status = check_filename(rename->rn_sname, rename->rn_snamelen, nfserr_noent)))
return status;
if ((status = check_filename(rename->rn_tname, rename->rn_tnamelen, nfserr_inval)))
......@@ -701,16 +773,16 @@ nfsd4_decode_setclientid(struct nfsd4_compoundargs *argp, struct nfsd4_setclient
READ32(setclientid->se_namelen);
READ_BUF(setclientid->se_namelen + 8);
READMEM(setclientid->se_name, setclientid->se_namelen);
SAVEMEM(setclientid->se_name, setclientid->se_namelen);
READ32(setclientid->se_callback_prog);
READ32(setclientid->se_callback_netid_len);
READ_BUF(setclientid->se_callback_netid_len + 4);
READMEM(setclientid->se_callback_netid_val, setclientid->se_callback_netid_len);
SAVEMEM(setclientid->se_callback_netid_val, setclientid->se_callback_netid_len);
READ32(setclientid->se_callback_addr_len);
READ_BUF(setclientid->se_callback_addr_len + 4);
READMEM(setclientid->se_callback_addr_val, setclientid->se_callback_addr_len);
SAVEMEM(setclientid->se_callback_addr_val, setclientid->se_callback_addr_len);
READ32(setclientid->se_callback_ident);
DECODE_TAIL;
......@@ -739,7 +811,7 @@ nfsd4_decode_verify(struct nfsd4_compoundargs *argp, struct nfsd4_verify *verify
READ_BUF(4);
READ32(verify->ve_attrlen);
READ_BUF(verify->ve_attrlen);
READMEM(verify->ve_attrval, verify->ve_attrlen);
SAVEMEM(verify->ve_attrval, verify->ve_attrlen);
DECODE_TAIL;
}
......@@ -747,6 +819,9 @@ nfsd4_decode_verify(struct nfsd4_compoundargs *argp, struct nfsd4_verify *verify
static int
nfsd4_decode_write(struct nfsd4_compoundargs *argp, struct nfsd4_write *write)
{
int avail;
int v;
int len;
DECODE_HEAD;
READ_BUF(sizeof(stateid_t) + 16);
......@@ -758,8 +833,36 @@ nfsd4_decode_write(struct nfsd4_compoundargs *argp, struct nfsd4_write *write)
goto xdr_error;
READ32(write->wr_buflen);
READ_BUF(write->wr_buflen);
READMEM(write->wr_buf, write->wr_buflen);
/* Sorry .. no magic macros for this.. *
* READ_BUF(write->wr_buflen);
* SAVEMEM(write->wr_buf, write->wr_buflen);
*/
avail = (char*)argp->end - (char*)argp->p;
if (avail + argp->pagelen < write->wr_buflen) {
printk(KERN_NOTICE "xdr error! (%s:%d)\n", __FILE__, __LINE__);
goto xdr_error;
}
write->wr_vec[0].iov_base = p;
write->wr_vec[0].iov_len = avail;
v = 0;
len = write->wr_buflen;
while (len > write->wr_vec[v].iov_len) {
len -= write->wr_vec[v].iov_len;
v++;
write->wr_vec[v].iov_base = page_address(argp->pagelist[0]);
argp->pagelist++;
if (argp->pagelen >= PAGE_SIZE) {
write->wr_vec[v].iov_len = PAGE_SIZE;
argp->pagelen -= PAGE_SIZE;
} else {
write->wr_vec[v].iov_len = argp->pagelen;
argp->pagelen = 0;
}
}
argp->end = (u32*) (write->wr_vec[v].iov_base + write->wr_vec[v].iov_len);
argp->p = (u32*) (write->wr_vec[v].iov_base + len);
write->wr_vec[v].iov_len = len;
write->wr_vlen = v+1;
DECODE_TAIL;
}
......@@ -780,7 +883,7 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
READ_BUF(4);
READ32(argp->taglen);
READ_BUF(argp->taglen + 8);
READMEM(argp->tag, argp->taglen);
SAVEMEM(argp->tag, argp->taglen);
READ32(argp->minorversion);
READ32(argp->opcnt);
......@@ -1891,13 +1994,29 @@ nfs4svc_decode_compoundargs(struct svc_rqst *rqstp, u32 *p, struct nfsd4_compoun
int status;
args->p = p;
args->end = rqstp->rq_argbuf.base + rqstp->rq_argbuf.buflen;
args->end = rqstp->rq_arg.head[0].iov_base + rqstp->rq_arg.head[0].iov_len;
args->pagelist = rqstp->rq_arg.pages;
args->pagelen = rqstp->rq_args.page_len;
args->tmpp = NULL;
args->to_free = NULL;
args->ops = args->iops;
status = nfsd4_decode_compound(args);
if (status && args->ops != args->iops) {
kfree(args->ops);
args->ops = args->iops;
if (status) {
if (args->ops != args->iops) {
kfree(args->ops);
args->ops = args->iops;
}
if (args->tmpp) {
kfree(args->tmpp);
args->tmpp = NULL;
}
while (args->to_free) {
struct tmpbuf *tb = args->to_free;
args->to_free = tb->next;
kfree(tb->buf);
kfree(tb);
}
}
return !status;
}
......
......@@ -249,7 +249,9 @@ struct nfsd4_write {
u64 wr_offset; /* request */
u32 wr_stable_how; /* request */
u32 wr_buflen; /* request */
char * wr_buf; /* request */
struct iovec wr_vec[RPCSVC_MAXPAGES]; /* request */
int wr_vlen;
u32 wr_bytes_written; /* response */
u32 wr_how_written; /* response */
nfs4_verifier wr_verifier; /* response */
......@@ -288,6 +290,14 @@ struct nfsd4_compoundargs {
/* scratch variables for XDR decode */
u32 * p;
u32 * end;
struct page ** pagelist;
int pagelen;
u32 tmp[8];
u32 * tmpp;
struct tmpbuf {
struct tmpbuf *next;
void *buf;
} *to_free;
u32 taglen;
char * tag;
......
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