aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/nfsd/nfs3proc.c10
-rw-r--r--fs/nfsd/nfs3xdr.c51
-rw-r--r--fs/nfsd/nfsproc.c14
-rw-r--r--fs/nfsd/nfsxdr.c49
-rw-r--r--fs/nfsd/xdr.h1
-rw-r--r--fs/nfsd/xdr3.h1
-rw-r--r--fs/nfsd/xdr4.h2
-rw-r--r--include/linux/sunrpc/svc.h2
-rw-r--r--net/sunrpc/svc.c67
9 files changed, 132 insertions, 65 deletions
diff --git a/fs/nfsd/nfs3proc.c b/fs/nfsd/nfs3proc.c
index 2dd95ebf4935..6259a4b8579f 100644
--- a/fs/nfsd/nfs3proc.c
+++ b/fs/nfsd/nfs3proc.c
@@ -283,6 +283,16 @@ nfsd3_proc_symlink(struct svc_rqst *rqstp)
283 struct nfsd3_diropres *resp = rqstp->rq_resp; 283 struct nfsd3_diropres *resp = rqstp->rq_resp;
284 __be32 nfserr; 284 __be32 nfserr;
285 285
286 if (argp->tlen == 0)
287 RETURN_STATUS(nfserr_inval);
288 if (argp->tlen > NFS3_MAXPATHLEN)
289 RETURN_STATUS(nfserr_nametoolong);
290
291 argp->tname = svc_fill_symlink_pathname(rqstp, &argp->first,
292 argp->tlen);
293 if (IS_ERR(argp->tname))
294 RETURN_STATUS(nfserrno(PTR_ERR(argp->tname)));
295
286 dprintk("nfsd: SYMLINK(3) %s %.*s -> %.*s\n", 296 dprintk("nfsd: SYMLINK(3) %s %.*s -> %.*s\n",
287 SVCFH_fmt(&argp->ffh), 297 SVCFH_fmt(&argp->ffh),
288 argp->flen, argp->fname, 298 argp->flen, argp->fname,
diff --git a/fs/nfsd/nfs3xdr.c b/fs/nfsd/nfs3xdr.c
index e19fc5d8bcb5..3192b544a441 100644
--- a/fs/nfsd/nfs3xdr.c
+++ b/fs/nfsd/nfs3xdr.c
@@ -481,51 +481,24 @@ int
481nfs3svc_decode_symlinkargs(struct svc_rqst *rqstp, __be32 *p) 481nfs3svc_decode_symlinkargs(struct svc_rqst *rqstp, __be32 *p)
482{ 482{
483 struct nfsd3_symlinkargs *args = rqstp->rq_argp; 483 struct nfsd3_symlinkargs *args = rqstp->rq_argp;
484 unsigned int len, avail; 484 char *base = (char *)p;
485 char *old, *new; 485 size_t dlen;
486 struct kvec *vec;
487 486
488 if (!(p = decode_fh(p, &args->ffh)) || 487 if (!(p = decode_fh(p, &args->ffh)) ||
489 !(p = decode_filename(p, &args->fname, &args->flen)) 488 !(p = decode_filename(p, &args->fname, &args->flen)))
490 )
491 return 0; 489 return 0;
492 p = decode_sattr3(p, &args->attrs); 490 p = decode_sattr3(p, &args->attrs);
493 491
494 /* now decode the pathname, which might be larger than the first page. 492 args->tlen = ntohl(*p++);
495 * As we have to check for nul's anyway, we copy it into a new page
496 * This page appears in the rq_res.pages list, but as pages_len is always
497 * 0, it won't get in the way
498 */
499 len = ntohl(*p++);
500 if (len == 0 || len > NFS3_MAXPATHLEN || len >= PAGE_SIZE)
501 return 0;
502 args->tname = new = page_address(*(rqstp->rq_next_page++));
503 args->tlen = len;
504 /* first copy and check from the first page */
505 old = (char*)p;
506 vec = &rqstp->rq_arg.head[0];
507 if ((void *)old > vec->iov_base + vec->iov_len)
508 return 0;
509 avail = vec->iov_len - (old - (char*)vec->iov_base);
510 while (len && avail && *old) {
511 *new++ = *old++;
512 len--;
513 avail--;
514 }
515 /* now copy next page if there is one */
516 if (len && !avail && rqstp->rq_arg.page_len) {
517 avail = min_t(unsigned int, rqstp->rq_arg.page_len, PAGE_SIZE);
518 old = page_address(rqstp->rq_arg.pages[0]);
519 }
520 while (len && avail && *old) {
521 *new++ = *old++;
522 len--;
523 avail--;
524 }
525 *new = '\0';
526 if (len)
527 return 0;
528 493
494 args->first.iov_base = p;
495 args->first.iov_len = rqstp->rq_arg.head[0].iov_len;
496 args->first.iov_len -= (char *)p - base;
497
498 dlen = args->first.iov_len + rqstp->rq_arg.page_len +
499 rqstp->rq_arg.tail[0].iov_len;
500 if (dlen < XDR_QUADLEN(args->tlen) << 2)
501 return 0;
529 return 1; 502 return 1;
530} 503}
531 504
diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c
index 1995ea6bfd2b..f107f9fa8e15 100644
--- a/fs/nfsd/nfsproc.c
+++ b/fs/nfsd/nfsproc.c
@@ -449,17 +449,19 @@ nfsd_proc_symlink(struct svc_rqst *rqstp)
449 struct svc_fh newfh; 449 struct svc_fh newfh;
450 __be32 nfserr; 450 __be32 nfserr;
451 451
452 if (argp->tlen > NFS_MAXPATHLEN)
453 return nfserr_nametoolong;
454
455 argp->tname = svc_fill_symlink_pathname(rqstp, &argp->first,
456 argp->tlen);
457 if (IS_ERR(argp->tname))
458 return nfserrno(PTR_ERR(argp->tname));
459
452 dprintk("nfsd: SYMLINK %s %.*s -> %.*s\n", 460 dprintk("nfsd: SYMLINK %s %.*s -> %.*s\n",
453 SVCFH_fmt(&argp->ffh), argp->flen, argp->fname, 461 SVCFH_fmt(&argp->ffh), argp->flen, argp->fname,
454 argp->tlen, argp->tname); 462 argp->tlen, argp->tname);
455 463
456 fh_init(&newfh, NFS_FHSIZE); 464 fh_init(&newfh, NFS_FHSIZE);
457 /*
458 * Crazy hack: the request fits in a page, and already-decoded
459 * attributes follow argp->tname, so it's safe to just write a
460 * null to ensure it's null-terminated:
461 */
462 argp->tname[argp->tlen] = '\0';
463 nfserr = nfsd_symlink(rqstp, &argp->ffh, argp->fname, argp->flen, 465 nfserr = nfsd_symlink(rqstp, &argp->ffh, argp->fname, argp->flen,
464 argp->tname, &newfh); 466 argp->tname, &newfh);
465 467
diff --git a/fs/nfsd/nfsxdr.c b/fs/nfsd/nfsxdr.c
index db24ae8b67e0..a43e8260520a 100644
--- a/fs/nfsd/nfsxdr.c
+++ b/fs/nfsd/nfsxdr.c
@@ -71,22 +71,6 @@ decode_filename(__be32 *p, char **namp, unsigned int *lenp)
71} 71}
72 72
73static __be32 * 73static __be32 *
74decode_pathname(__be32 *p, char **namp, unsigned int *lenp)
75{
76 char *name;
77 unsigned int i;
78
79 if ((p = xdr_decode_string_inplace(p, namp, lenp, NFS_MAXPATHLEN)) != NULL) {
80 for (i = 0, name = *namp; i < *lenp; i++, name++) {
81 if (*name == '\0')
82 return NULL;
83 }
84 }
85
86 return p;
87}
88
89static __be32 *
90decode_sattr(__be32 *p, struct iattr *iap) 74decode_sattr(__be32 *p, struct iattr *iap)
91{ 75{
92 u32 tmp, tmp1; 76 u32 tmp, tmp1;
@@ -384,14 +368,39 @@ int
384nfssvc_decode_symlinkargs(struct svc_rqst *rqstp, __be32 *p) 368nfssvc_decode_symlinkargs(struct svc_rqst *rqstp, __be32 *p)
385{ 369{
386 struct nfsd_symlinkargs *args = rqstp->rq_argp; 370 struct nfsd_symlinkargs *args = rqstp->rq_argp;
371 char *base = (char *)p;
372 size_t xdrlen;
387 373
388 if ( !(p = decode_fh(p, &args->ffh)) 374 if ( !(p = decode_fh(p, &args->ffh))
389 || !(p = decode_filename(p, &args->fname, &args->flen)) 375 || !(p = decode_filename(p, &args->fname, &args->flen)))
390 || !(p = decode_pathname(p, &args->tname, &args->tlen)))
391 return 0; 376 return 0;
392 p = decode_sattr(p, &args->attrs);
393 377
394 return xdr_argsize_check(rqstp, p); 378 args->tlen = ntohl(*p++);
379 if (args->tlen == 0)
380 return 0;
381
382 args->first.iov_base = p;
383 args->first.iov_len = rqstp->rq_arg.head[0].iov_len;
384 args->first.iov_len -= (char *)p - base;
385
386 /* This request is never larger than a page. Therefore,
387 * transport will deliver either:
388 * 1. pathname in the pagelist -> sattr is in the tail.
389 * 2. everything in the head buffer -> sattr is in the head.
390 */
391 if (rqstp->rq_arg.page_len) {
392 if (args->tlen != rqstp->rq_arg.page_len)
393 return 0;
394 p = rqstp->rq_arg.tail[0].iov_base;
395 } else {
396 xdrlen = XDR_QUADLEN(args->tlen);
397 if (xdrlen > args->first.iov_len - (8 * sizeof(__be32)))
398 return 0;
399 p += xdrlen;
400 }
401 decode_sattr(p, &args->attrs);
402
403 return 1;
395} 404}
396 405
397int 406int
diff --git a/fs/nfsd/xdr.h b/fs/nfsd/xdr.h
index a765c414015e..ea7cca3a64b7 100644
--- a/fs/nfsd/xdr.h
+++ b/fs/nfsd/xdr.h
@@ -72,6 +72,7 @@ struct nfsd_symlinkargs {
72 char * tname; 72 char * tname;
73 unsigned int tlen; 73 unsigned int tlen;
74 struct iattr attrs; 74 struct iattr attrs;
75 struct kvec first;
75}; 76};
76 77
77struct nfsd_readdirargs { 78struct nfsd_readdirargs {
diff --git a/fs/nfsd/xdr3.h b/fs/nfsd/xdr3.h
index deccf7f691e9..2cb29e961a76 100644
--- a/fs/nfsd/xdr3.h
+++ b/fs/nfsd/xdr3.h
@@ -90,6 +90,7 @@ struct nfsd3_symlinkargs {
90 char * tname; 90 char * tname;
91 unsigned int tlen; 91 unsigned int tlen;
92 struct iattr attrs; 92 struct iattr attrs;
93 struct kvec first;
93}; 94};
94 95
95struct nfsd3_readdirargs { 96struct nfsd3_readdirargs {
diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
index 7cbc129092fe..468020fe2a07 100644
--- a/fs/nfsd/xdr4.h
+++ b/fs/nfsd/xdr4.h
@@ -110,6 +110,7 @@ struct nfsd4_create {
110 struct { 110 struct {
111 u32 datalen; 111 u32 datalen;
112 char *data; 112 char *data;
113 struct kvec first;
113 } link; /* NF4LNK */ 114 } link; /* NF4LNK */
114 struct { 115 struct {
115 u32 specdata1; 116 u32 specdata1;
@@ -124,6 +125,7 @@ struct nfsd4_create {
124}; 125};
125#define cr_datalen u.link.datalen 126#define cr_datalen u.link.datalen
126#define cr_data u.link.data 127#define cr_data u.link.data
128#define cr_first u.link.first
127#define cr_specdata1 u.dev.specdata1 129#define cr_specdata1 u.dev.specdata1
128#define cr_specdata2 u.dev.specdata2 130#define cr_specdata2 u.dev.specdata2
129 131
diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h
index fb3fcacc1e98..574368e8a16f 100644
--- a/include/linux/sunrpc/svc.h
+++ b/include/linux/sunrpc/svc.h
@@ -497,6 +497,8 @@ struct svc_pool * svc_pool_for_cpu(struct svc_serv *serv, int cpu);
497char * svc_print_addr(struct svc_rqst *, char *, size_t); 497char * svc_print_addr(struct svc_rqst *, char *, size_t);
498unsigned int svc_fill_write_vector(struct svc_rqst *rqstp, 498unsigned int svc_fill_write_vector(struct svc_rqst *rqstp,
499 struct kvec *first, size_t total); 499 struct kvec *first, size_t total);
500char *svc_fill_symlink_pathname(struct svc_rqst *rqstp,
501 struct kvec *first, size_t total);
500 502
501#define RPC_MAX_ADDRBUFLEN (63U) 503#define RPC_MAX_ADDRBUFLEN (63U)
502 504
diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c
index a155e2de19aa..30a4226baf03 100644
--- a/net/sunrpc/svc.c
+++ b/net/sunrpc/svc.c
@@ -1575,3 +1575,70 @@ unsigned int svc_fill_write_vector(struct svc_rqst *rqstp, struct kvec *first,
1575 return i; 1575 return i;
1576} 1576}
1577EXPORT_SYMBOL_GPL(svc_fill_write_vector); 1577EXPORT_SYMBOL_GPL(svc_fill_write_vector);
1578
1579/**
1580 * svc_fill_symlink_pathname - Construct pathname argument for VFS symlink call
1581 * @rqstp: svc_rqst to operate on
1582 * @first: buffer containing first section of pathname
1583 * @total: total length of the pathname argument
1584 *
1585 * Returns pointer to a NUL-terminated string, or an ERR_PTR. The buffer is
1586 * released automatically when @rqstp is recycled.
1587 */
1588char *svc_fill_symlink_pathname(struct svc_rqst *rqstp, struct kvec *first,
1589 size_t total)
1590{
1591 struct xdr_buf *arg = &rqstp->rq_arg;
1592 struct page **pages;
1593 char *result;
1594
1595 /* VFS API demands a NUL-terminated pathname. This function
1596 * uses a page from @rqstp as the pathname buffer, to enable
1597 * direct placement. Thus the total buffer size is PAGE_SIZE.
1598 * Space in this buffer for NUL-termination requires that we
1599 * cap the size of the returned symlink pathname just a
1600 * little early.
1601 */
1602 if (total > PAGE_SIZE - 1)
1603 return ERR_PTR(-ENAMETOOLONG);
1604
1605 /* Some types of transport can present the pathname entirely
1606 * in rq_arg.pages. If not, then copy the pathname into one
1607 * page.
1608 */
1609 pages = arg->pages;
1610 WARN_ON_ONCE(arg->page_base != 0);
1611 if (first->iov_base == 0) {
1612 result = page_address(*pages);
1613 result[total] = '\0';
1614 } else {
1615 size_t len, remaining;
1616 char *dst;
1617
1618 result = page_address(*(rqstp->rq_next_page++));
1619 dst = result;
1620 remaining = total;
1621
1622 len = min_t(size_t, total, first->iov_len);
1623 memcpy(dst, first->iov_base, len);
1624 dst += len;
1625 remaining -= len;
1626
1627 /* No more than one page left */
1628 if (remaining) {
1629 len = min_t(size_t, remaining, PAGE_SIZE);
1630 memcpy(dst, page_address(*pages), len);
1631 dst += len;
1632 }
1633
1634 *dst = '\0';
1635 }
1636
1637 /* Sanity check: we don't allow the pathname argument to
1638 * contain a NUL byte.
1639 */
1640 if (strlen(result) != total)
1641 return ERR_PTR(-EINVAL);
1642 return result;
1643}
1644EXPORT_SYMBOL_GPL(svc_fill_symlink_pathname);