aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/nfsd/nfs4proc.c9
-rw-r--r--fs/nfsd/nfs4xdr.c137
-rw-r--r--fs/nfsd/xdr4.h5
3 files changed, 82 insertions, 69 deletions
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index 59c319528cf9..d95d9015e50a 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -1500,13 +1500,14 @@ static inline u32 nfsd4_read_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
1500 1500
1501static inline u32 nfsd4_readdir_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op) 1501static inline u32 nfsd4_readdir_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
1502{ 1502{
1503 u32 maxcount = svc_max_payload(rqstp);
1503 u32 rlen = op->u.readdir.rd_maxcount; 1504 u32 rlen = op->u.readdir.rd_maxcount;
1504 1505
1505 if (rlen > PAGE_SIZE) 1506 if (rlen > maxcount)
1506 rlen = PAGE_SIZE; 1507 rlen = maxcount;
1507 1508
1508 return (op_encode_hdr_size + op_encode_verifier_maxsz) 1509 return (op_encode_hdr_size + op_encode_verifier_maxsz +
1509 * sizeof(__be32) + rlen; 1510 XDR_QUADLEN(rlen)) * sizeof(__be32);
1510} 1511}
1511 1512
1512static inline u32 nfsd4_remove_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op) 1513static inline u32 nfsd4_remove_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 5e7bac4a7e71..4d79e5366a82 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -2575,8 +2575,8 @@ static inline int attributes_need_mount(u32 *bmval)
2575} 2575}
2576 2576
2577static __be32 2577static __be32
2578nfsd4_encode_dirent_fattr(struct nfsd4_readdir *cd, 2578nfsd4_encode_dirent_fattr(struct xdr_stream *xdr, struct nfsd4_readdir *cd,
2579 const char *name, int namlen, __be32 **p, int buflen) 2579 const char *name, int namlen)
2580{ 2580{
2581 struct svc_export *exp = cd->rd_fhp->fh_export; 2581 struct svc_export *exp = cd->rd_fhp->fh_export;
2582 struct dentry *dentry; 2582 struct dentry *dentry;
@@ -2628,8 +2628,7 @@ nfsd4_encode_dirent_fattr(struct nfsd4_readdir *cd,
2628 2628
2629 } 2629 }
2630out_encode: 2630out_encode:
2631 nfserr = nfsd4_encode_fattr_to_buf(p, buflen, NULL, exp, dentry, 2631 nfserr = nfsd4_encode_fattr(xdr, NULL, exp, dentry, cd->rd_bmval,
2632 cd->rd_bmval,
2633 cd->rd_rqstp, ignore_crossmnt); 2632 cd->rd_rqstp, ignore_crossmnt);
2634out_put: 2633out_put:
2635 dput(dentry); 2634 dput(dentry);
@@ -2638,9 +2637,12 @@ out_put:
2638} 2637}
2639 2638
2640static __be32 * 2639static __be32 *
2641nfsd4_encode_rdattr_error(__be32 *p, int buflen, __be32 nfserr) 2640nfsd4_encode_rdattr_error(struct xdr_stream *xdr, __be32 nfserr)
2642{ 2641{
2643 if (buflen < 6) 2642 __be32 *p;
2643
2644 p = xdr_reserve_space(xdr, 6);
2645 if (!p)
2644 return NULL; 2646 return NULL;
2645 *p++ = htonl(2); 2647 *p++ = htonl(2);
2646 *p++ = htonl(FATTR4_WORD0_RDATTR_ERROR); /* bmval0 */ 2648 *p++ = htonl(FATTR4_WORD0_RDATTR_ERROR); /* bmval0 */
@@ -2657,10 +2659,13 @@ nfsd4_encode_dirent(void *ccdv, const char *name, int namlen,
2657{ 2659{
2658 struct readdir_cd *ccd = ccdv; 2660 struct readdir_cd *ccd = ccdv;
2659 struct nfsd4_readdir *cd = container_of(ccd, struct nfsd4_readdir, common); 2661 struct nfsd4_readdir *cd = container_of(ccd, struct nfsd4_readdir, common);
2660 int buflen; 2662 struct xdr_stream *xdr = cd->xdr;
2661 __be32 *p = cd->buffer; 2663 int start_offset = xdr->buf->len;
2662 __be32 *cookiep; 2664 int cookie_offset;
2665 int entry_bytes;
2663 __be32 nfserr = nfserr_toosmall; 2666 __be32 nfserr = nfserr_toosmall;
2667 __be64 wire_offset;
2668 __be32 *p;
2664 2669
2665 /* In nfsv4, "." and ".." never make it onto the wire.. */ 2670 /* In nfsv4, "." and ".." never make it onto the wire.. */
2666 if (name && isdotent(name, namlen)) { 2671 if (name && isdotent(name, namlen)) {
@@ -2668,19 +2673,24 @@ nfsd4_encode_dirent(void *ccdv, const char *name, int namlen,
2668 return 0; 2673 return 0;
2669 } 2674 }
2670 2675
2671 if (cd->offset) 2676 if (cd->cookie_offset) {
2672 xdr_encode_hyper(cd->offset, (u64) offset); 2677 wire_offset = cpu_to_be64(offset);
2678 write_bytes_to_xdr_buf(xdr->buf, cd->cookie_offset,
2679 &wire_offset, 8);
2680 }
2673 2681
2674 buflen = cd->buflen - 4 - XDR_QUADLEN(namlen); 2682 p = xdr_reserve_space(xdr, 4);
2675 if (buflen < 0) 2683 if (!p)
2676 goto fail; 2684 goto fail;
2677
2678 *p++ = xdr_one; /* mark entry present */ 2685 *p++ = xdr_one; /* mark entry present */
2679 cookiep = p; 2686 cookie_offset = xdr->buf->len;
2687 p = xdr_reserve_space(xdr, 3*4 + namlen);
2688 if (!p)
2689 goto fail;
2680 p = xdr_encode_hyper(p, NFS_OFFSET_MAX); /* offset of next entry */ 2690 p = xdr_encode_hyper(p, NFS_OFFSET_MAX); /* offset of next entry */
2681 p = xdr_encode_array(p, name, namlen); /* name length & name */ 2691 p = xdr_encode_array(p, name, namlen); /* name length & name */
2682 2692
2683 nfserr = nfsd4_encode_dirent_fattr(cd, name, namlen, &p, buflen); 2693 nfserr = nfsd4_encode_dirent_fattr(xdr, cd, name, namlen);
2684 switch (nfserr) { 2694 switch (nfserr) {
2685 case nfs_ok: 2695 case nfs_ok:
2686 break; 2696 break;
@@ -2699,19 +2709,23 @@ nfsd4_encode_dirent(void *ccdv, const char *name, int namlen,
2699 */ 2709 */
2700 if (!(cd->rd_bmval[0] & FATTR4_WORD0_RDATTR_ERROR)) 2710 if (!(cd->rd_bmval[0] & FATTR4_WORD0_RDATTR_ERROR))
2701 goto fail; 2711 goto fail;
2702 p = nfsd4_encode_rdattr_error(p, buflen, nfserr); 2712 p = nfsd4_encode_rdattr_error(xdr, nfserr);
2703 if (p == NULL) { 2713 if (p == NULL) {
2704 nfserr = nfserr_toosmall; 2714 nfserr = nfserr_toosmall;
2705 goto fail; 2715 goto fail;
2706 } 2716 }
2707 } 2717 }
2708 cd->buflen -= (p - cd->buffer); 2718 nfserr = nfserr_toosmall;
2709 cd->buffer = p; 2719 entry_bytes = xdr->buf->len - start_offset;
2710 cd->offset = cookiep; 2720 if (entry_bytes > cd->rd_maxcount)
2721 goto fail;
2722 cd->rd_maxcount -= entry_bytes;
2723 cd->cookie_offset = cookie_offset;
2711skip_entry: 2724skip_entry:
2712 cd->common.err = nfs_ok; 2725 cd->common.err = nfs_ok;
2713 return 0; 2726 return 0;
2714fail: 2727fail:
2728 xdr_truncate_encode(xdr, start_offset);
2715 cd->common.err = nfserr; 2729 cd->common.err = nfserr;
2716 return -EINVAL; 2730 return -EINVAL;
2717} 2731}
@@ -3206,10 +3220,11 @@ static __be32
3206nfsd4_encode_readdir(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_readdir *readdir) 3220nfsd4_encode_readdir(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_readdir *readdir)
3207{ 3221{
3208 int maxcount; 3222 int maxcount;
3223 int bytes_left;
3209 loff_t offset; 3224 loff_t offset;
3225 __be64 wire_offset;
3210 struct xdr_stream *xdr = &resp->xdr; 3226 struct xdr_stream *xdr = &resp->xdr;
3211 int starting_len = xdr->buf->len; 3227 int starting_len = xdr->buf->len;
3212 __be32 *page, *tailbase;
3213 __be32 *p; 3228 __be32 *p;
3214 3229
3215 if (nfserr) 3230 if (nfserr)
@@ -3219,38 +3234,38 @@ nfsd4_encode_readdir(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4
3219 if (!p) 3234 if (!p)
3220 return nfserr_resource; 3235 return nfserr_resource;
3221 3236
3222 if (resp->xdr.buf->page_len)
3223 return nfserr_resource;
3224 if (!*resp->rqstp->rq_next_page)
3225 return nfserr_resource;
3226
3227 /* XXX: Following NFSv3, we ignore the READDIR verifier for now. */ 3237 /* XXX: Following NFSv3, we ignore the READDIR verifier for now. */
3228 WRITE32(0); 3238 WRITE32(0);
3229 WRITE32(0); 3239 WRITE32(0);
3230 resp->xdr.buf->head[0].iov_len = ((char *)resp->xdr.p) 3240 resp->xdr.buf->head[0].iov_len = ((char *)resp->xdr.p)
3231 - (char *)resp->xdr.buf->head[0].iov_base; 3241 - (char *)resp->xdr.buf->head[0].iov_base;
3232 tailbase = p;
3233
3234 maxcount = PAGE_SIZE;
3235 if (maxcount > readdir->rd_maxcount)
3236 maxcount = readdir->rd_maxcount;
3237 3242
3238 /* 3243 /*
3239 * Convert from bytes to words, account for the two words already 3244 * Number of bytes left for directory entries allowing for the
3240 * written, make sure to leave two words at the end for the next 3245 * final 8 bytes of the readdir and a following failed op:
3241 * pointer and eof field. 3246 */
3247 bytes_left = xdr->buf->buflen - xdr->buf->len
3248 - COMPOUND_ERR_SLACK_SPACE - 8;
3249 if (bytes_left < 0) {
3250 nfserr = nfserr_resource;
3251 goto err_no_verf;
3252 }
3253 maxcount = min_t(u32, readdir->rd_maxcount, INT_MAX);
3254 /*
3255 * Note the rfc defines rd_maxcount as the size of the
3256 * READDIR4resok structure, which includes the verifier above
3257 * and the 8 bytes encoded at the end of this function:
3242 */ 3258 */
3243 maxcount = (maxcount >> 2) - 4; 3259 if (maxcount < 16) {
3244 if (maxcount < 0) { 3260 nfserr = nfserr_toosmall;
3245 nfserr = nfserr_toosmall;
3246 goto err_no_verf; 3261 goto err_no_verf;
3247 } 3262 }
3263 maxcount = min_t(int, maxcount-16, bytes_left);
3248 3264
3249 page = page_address(*(resp->rqstp->rq_next_page++)); 3265 readdir->xdr = xdr;
3266 readdir->rd_maxcount = maxcount;
3250 readdir->common.err = 0; 3267 readdir->common.err = 0;
3251 readdir->buflen = maxcount; 3268 readdir->cookie_offset = 0;
3252 readdir->buffer = page;
3253 readdir->offset = NULL;
3254 3269
3255 offset = readdir->rd_cookie; 3270 offset = readdir->rd_cookie;
3256 nfserr = nfsd_readdir(readdir->rd_rqstp, readdir->rd_fhp, 3271 nfserr = nfsd_readdir(readdir->rd_rqstp, readdir->rd_fhp,
@@ -3258,33 +3273,31 @@ nfsd4_encode_readdir(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4
3258 &readdir->common, nfsd4_encode_dirent); 3273 &readdir->common, nfsd4_encode_dirent);
3259 if (nfserr == nfs_ok && 3274 if (nfserr == nfs_ok &&
3260 readdir->common.err == nfserr_toosmall && 3275 readdir->common.err == nfserr_toosmall &&
3261 readdir->buffer == page) 3276 xdr->buf->len == starting_len + 8) {
3262 nfserr = nfserr_toosmall; 3277 /* nothing encoded; which limit did we hit?: */
3278 if (maxcount - 16 < bytes_left)
3279 /* It was the fault of rd_maxcount: */
3280 nfserr = nfserr_toosmall;
3281 else
3282 /* We ran out of buffer space: */
3283 nfserr = nfserr_resource;
3284 }
3263 if (nfserr) 3285 if (nfserr)
3264 goto err_no_verf; 3286 goto err_no_verf;
3265 3287
3266 if (readdir->offset) 3288 if (readdir->cookie_offset) {
3267 xdr_encode_hyper(readdir->offset, offset); 3289 wire_offset = cpu_to_be64(offset);
3290 write_bytes_to_xdr_buf(xdr->buf, readdir->cookie_offset,
3291 &wire_offset, 8);
3292 }
3268 3293
3269 p = readdir->buffer; 3294 p = xdr_reserve_space(xdr, 8);
3295 if (!p) {
3296 WARN_ON_ONCE(1);
3297 goto err_no_verf;
3298 }
3270 *p++ = 0; /* no more entries */ 3299 *p++ = 0; /* no more entries */
3271 *p++ = htonl(readdir->common.err == nfserr_eof); 3300 *p++ = htonl(readdir->common.err == nfserr_eof);
3272 resp->xdr.buf->page_len = ((char *)p) -
3273 (char*)page_address(*(resp->rqstp->rq_next_page-1));
3274 xdr->buf->len += xdr->buf->page_len;
3275
3276 xdr->iov = xdr->buf->tail;
3277
3278 xdr->page_ptr++;
3279 xdr->buf->buflen -= PAGE_SIZE;
3280 xdr->iov = xdr->buf->tail;
3281
3282 /* Use rest of head for padding and remaining ops: */
3283 resp->xdr.buf->tail[0].iov_base = tailbase;
3284 resp->xdr.buf->tail[0].iov_len = 0;
3285 resp->xdr.p = resp->xdr.buf->tail[0].iov_base;
3286 resp->xdr.end = resp->xdr.p +
3287 (PAGE_SIZE - resp->xdr.buf->head[0].iov_len)/4;
3288 3301
3289 return 0; 3302 return 0;
3290err_no_verf: 3303err_no_verf:
diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
index 41e522993d94..18cbb6d9c8a9 100644
--- a/fs/nfsd/xdr4.h
+++ b/fs/nfsd/xdr4.h
@@ -287,9 +287,8 @@ struct nfsd4_readdir {
287 struct svc_fh * rd_fhp; /* response */ 287 struct svc_fh * rd_fhp; /* response */
288 288
289 struct readdir_cd common; 289 struct readdir_cd common;
290 __be32 * buffer; 290 struct xdr_stream *xdr;
291 int buflen; 291 int cookie_offset;
292 __be32 * offset;
293}; 292};
294 293
295struct nfsd4_release_lockowner { 294struct nfsd4_release_lockowner {