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 ...@@ -481,7 +481,8 @@ nfsd4_write(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_writ
*p++ = nfssvc_boot.tv_usec; *p++ = nfssvc_boot.tv_usec;
return nfsd_write(rqstp, current_fh, write->wr_offset, 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 /* This routine never returns NFS_OK! If there are no other errors, it
...@@ -700,6 +701,16 @@ nfsd4_proc_compound(struct svc_rqst *rqstp, ...@@ -700,6 +701,16 @@ nfsd4_proc_compound(struct svc_rqst *rqstp,
kfree(args->ops); kfree(args->ops);
args->ops = args->iops; 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(&current_fh);
fh_put(&save_fh); fh_put(&save_fh);
return status; return status;
......
...@@ -217,20 +217,92 @@ xdr_error: \ ...@@ -217,20 +217,92 @@ xdr_error: \
x = (char *)p; \ x = (char *)p; \
p += XDR_QUADLEN(nbytes); \ p += XDR_QUADLEN(nbytes); \
} while (0) } 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 { \ #define COPYMEM(x,nbytes) do { \
memcpy((x), p, nbytes); \ memcpy((x), p, nbytes); \
p += XDR_QUADLEN(nbytes); \ p += XDR_QUADLEN(nbytes); \
} while (0) } while (0)
#define READ_BUF(nbytes) do { \ #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__); \ printk(KERN_NOTICE "xdr error! (%s:%d)\n", __FILE__, __LINE__); \
goto xdr_error; \ goto xdr_error; \
} \ } \
p = argp->p; \
argp->p += XDR_QUADLEN(nbytes); \
} while (0) } 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 static int
nfsd4_decode_bitmap(struct nfsd4_compoundargs *argp, u32 *bmval) nfsd4_decode_bitmap(struct nfsd4_compoundargs *argp, u32 *bmval)
{ {
...@@ -442,7 +514,7 @@ nfsd4_decode_create(struct nfsd4_compoundargs *argp, struct nfsd4_create *create ...@@ -442,7 +514,7 @@ nfsd4_decode_create(struct nfsd4_compoundargs *argp, struct nfsd4_create *create
READ_BUF(4); READ_BUF(4);
READ32(create->cr_linklen); READ32(create->cr_linklen);
READ_BUF(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)) if (check_utf8(create->cr_linkname, create->cr_linklen))
return nfserr_inval; return nfserr_inval;
break; break;
...@@ -463,7 +535,7 @@ nfsd4_decode_create(struct nfsd4_compoundargs *argp, struct nfsd4_create *create ...@@ -463,7 +535,7 @@ nfsd4_decode_create(struct nfsd4_compoundargs *argp, struct nfsd4_create *create
READ_BUF(4); READ_BUF(4);
READ32(create->cr_namelen); READ32(create->cr_namelen);
READ_BUF(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))) if ((status = check_filename(create->cr_name, create->cr_namelen, nfserr_inval)))
return status; return status;
...@@ -487,7 +559,7 @@ nfsd4_decode_link(struct nfsd4_compoundargs *argp, struct nfsd4_link *link) ...@@ -487,7 +559,7 @@ nfsd4_decode_link(struct nfsd4_compoundargs *argp, struct nfsd4_link *link)
READ_BUF(4); READ_BUF(4);
READ32(link->li_namelen); READ32(link->li_namelen);
READ_BUF(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))) if ((status = check_filename(link->li_name, link->li_namelen, nfserr_inval)))
return status; return status;
...@@ -502,7 +574,7 @@ nfsd4_decode_lookup(struct nfsd4_compoundargs *argp, struct nfsd4_lookup *lookup ...@@ -502,7 +574,7 @@ nfsd4_decode_lookup(struct nfsd4_compoundargs *argp, struct nfsd4_lookup *lookup
READ_BUF(4); READ_BUF(4);
READ32(lookup->lo_len); READ32(lookup->lo_len);
READ_BUF(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))) if ((status = check_filename(lookup->lo_name, lookup->lo_len, nfserr_noent)))
return status; return status;
...@@ -527,7 +599,7 @@ nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open) ...@@ -527,7 +599,7 @@ nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open)
/* owner, open_flag */ /* owner, open_flag */
READ_BUF(open->op_ownerlen + 4); READ_BUF(open->op_ownerlen + 4);
READMEM(open->op_owner, open->op_ownerlen); SAVEMEM(open->op_owner, open->op_ownerlen);
READ32(open->op_create); READ32(open->op_create);
switch (open->op_create) { switch (open->op_create) {
case NFS4_OPEN_NOCREATE: case NFS4_OPEN_NOCREATE:
...@@ -562,7 +634,7 @@ nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open) ...@@ -562,7 +634,7 @@ nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open)
READ_BUF(4); READ_BUF(4);
READ32(open->op_namelen); READ32(open->op_namelen);
READ_BUF(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))) if ((status = check_filename(open->op_name, open->op_namelen, nfserr_inval)))
return status; return status;
break; break;
...@@ -575,7 +647,7 @@ nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open) ...@@ -575,7 +647,7 @@ nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open)
COPYMEM(&open->op_delegate_stateid, sizeof(delegation_stateid_t)); COPYMEM(&open->op_delegate_stateid, sizeof(delegation_stateid_t));
READ32(open->op_namelen); READ32(open->op_namelen);
READ_BUF(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))) if ((status = check_filename(open->op_name, open->op_namelen, nfserr_inval)))
return status; return status;
break; break;
...@@ -596,7 +668,7 @@ nfsd4_decode_putfh(struct nfsd4_compoundargs *argp, struct nfsd4_putfh *putfh) ...@@ -596,7 +668,7 @@ nfsd4_decode_putfh(struct nfsd4_compoundargs *argp, struct nfsd4_putfh *putfh)
if (putfh->pf_fhlen > NFS4_FHSIZE) if (putfh->pf_fhlen > NFS4_FHSIZE)
goto xdr_error; goto xdr_error;
READ_BUF(putfh->pf_fhlen); READ_BUF(putfh->pf_fhlen);
READMEM(putfh->pf_fhval, putfh->pf_fhlen); SAVEMEM(putfh->pf_fhval, putfh->pf_fhlen);
DECODE_TAIL; DECODE_TAIL;
} }
...@@ -639,7 +711,7 @@ nfsd4_decode_remove(struct nfsd4_compoundargs *argp, struct nfsd4_remove *remove ...@@ -639,7 +711,7 @@ nfsd4_decode_remove(struct nfsd4_compoundargs *argp, struct nfsd4_remove *remove
READ_BUF(4); READ_BUF(4);
READ32(remove->rm_namelen); READ32(remove->rm_namelen);
READ_BUF(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))) if ((status = check_filename(remove->rm_name, remove->rm_namelen, nfserr_noent)))
return status; return status;
...@@ -654,10 +726,10 @@ nfsd4_decode_rename(struct nfsd4_compoundargs *argp, struct nfsd4_rename *rename ...@@ -654,10 +726,10 @@ nfsd4_decode_rename(struct nfsd4_compoundargs *argp, struct nfsd4_rename *rename
READ_BUF(4); READ_BUF(4);
READ32(rename->rn_snamelen); READ32(rename->rn_snamelen);
READ_BUF(rename->rn_snamelen + 4); READ_BUF(rename->rn_snamelen + 4);
READMEM(rename->rn_sname, rename->rn_snamelen); SAVEMEM(rename->rn_sname, rename->rn_snamelen);
READ32(rename->rn_tnamelen); READ32(rename->rn_tnamelen);
READ_BUF(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))) if ((status = check_filename(rename->rn_sname, rename->rn_snamelen, nfserr_noent)))
return status; return status;
if ((status = check_filename(rename->rn_tname, rename->rn_tnamelen, nfserr_inval))) 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 ...@@ -701,16 +773,16 @@ nfsd4_decode_setclientid(struct nfsd4_compoundargs *argp, struct nfsd4_setclient
READ32(setclientid->se_namelen); READ32(setclientid->se_namelen);
READ_BUF(setclientid->se_namelen + 8); 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_prog);
READ32(setclientid->se_callback_netid_len); READ32(setclientid->se_callback_netid_len);
READ_BUF(setclientid->se_callback_netid_len + 4); 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); READ32(setclientid->se_callback_addr_len);
READ_BUF(setclientid->se_callback_addr_len + 4); 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); READ32(setclientid->se_callback_ident);
DECODE_TAIL; DECODE_TAIL;
...@@ -739,7 +811,7 @@ nfsd4_decode_verify(struct nfsd4_compoundargs *argp, struct nfsd4_verify *verify ...@@ -739,7 +811,7 @@ nfsd4_decode_verify(struct nfsd4_compoundargs *argp, struct nfsd4_verify *verify
READ_BUF(4); READ_BUF(4);
READ32(verify->ve_attrlen); READ32(verify->ve_attrlen);
READ_BUF(verify->ve_attrlen); READ_BUF(verify->ve_attrlen);
READMEM(verify->ve_attrval, verify->ve_attrlen); SAVEMEM(verify->ve_attrval, verify->ve_attrlen);
DECODE_TAIL; DECODE_TAIL;
} }
...@@ -747,6 +819,9 @@ nfsd4_decode_verify(struct nfsd4_compoundargs *argp, struct nfsd4_verify *verify ...@@ -747,6 +819,9 @@ nfsd4_decode_verify(struct nfsd4_compoundargs *argp, struct nfsd4_verify *verify
static int static int
nfsd4_decode_write(struct nfsd4_compoundargs *argp, struct nfsd4_write *write) nfsd4_decode_write(struct nfsd4_compoundargs *argp, struct nfsd4_write *write)
{ {
int avail;
int v;
int len;
DECODE_HEAD; DECODE_HEAD;
READ_BUF(sizeof(stateid_t) + 16); READ_BUF(sizeof(stateid_t) + 16);
...@@ -758,8 +833,36 @@ nfsd4_decode_write(struct nfsd4_compoundargs *argp, struct nfsd4_write *write) ...@@ -758,8 +833,36 @@ nfsd4_decode_write(struct nfsd4_compoundargs *argp, struct nfsd4_write *write)
goto xdr_error; goto xdr_error;
READ32(write->wr_buflen); READ32(write->wr_buflen);
READ_BUF(write->wr_buflen); /* Sorry .. no magic macros for this.. *
READMEM(write->wr_buf, write->wr_buflen); * 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; DECODE_TAIL;
} }
...@@ -780,7 +883,7 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp) ...@@ -780,7 +883,7 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
READ_BUF(4); READ_BUF(4);
READ32(argp->taglen); READ32(argp->taglen);
READ_BUF(argp->taglen + 8); READ_BUF(argp->taglen + 8);
READMEM(argp->tag, argp->taglen); SAVEMEM(argp->tag, argp->taglen);
READ32(argp->minorversion); READ32(argp->minorversion);
READ32(argp->opcnt); READ32(argp->opcnt);
...@@ -1891,13 +1994,29 @@ nfs4svc_decode_compoundargs(struct svc_rqst *rqstp, u32 *p, struct nfsd4_compoun ...@@ -1891,13 +1994,29 @@ nfs4svc_decode_compoundargs(struct svc_rqst *rqstp, u32 *p, struct nfsd4_compoun
int status; int status;
args->p = p; 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; args->ops = args->iops;
status = nfsd4_decode_compound(args); status = nfsd4_decode_compound(args);
if (status && args->ops != args->iops) { if (status) {
kfree(args->ops); if (args->ops != args->iops) {
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; return !status;
} }
......
...@@ -249,7 +249,9 @@ struct nfsd4_write { ...@@ -249,7 +249,9 @@ struct nfsd4_write {
u64 wr_offset; /* request */ u64 wr_offset; /* request */
u32 wr_stable_how; /* request */ u32 wr_stable_how; /* request */
u32 wr_buflen; /* 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_bytes_written; /* response */
u32 wr_how_written; /* response */ u32 wr_how_written; /* response */
nfs4_verifier wr_verifier; /* response */ nfs4_verifier wr_verifier; /* response */
...@@ -288,6 +290,14 @@ struct nfsd4_compoundargs { ...@@ -288,6 +290,14 @@ struct nfsd4_compoundargs {
/* scratch variables for XDR decode */ /* scratch variables for XDR decode */
u32 * p; u32 * p;
u32 * end; u32 * end;
struct page ** pagelist;
int pagelen;
u32 tmp[8];
u32 * tmpp;
struct tmpbuf {
struct tmpbuf *next;
void *buf;
} *to_free;
u32 taglen; u32 taglen;
char * tag; 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