diff options
-rw-r--r-- | fs/nfsd/nfs4proc.c | 9 | ||||
-rw-r--r-- | fs/nfsd/nfs4xdr.c | 137 | ||||
-rw-r--r-- | fs/nfsd/xdr4.h | 5 |
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 | ||
1501 | static inline u32 nfsd4_readdir_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op) | 1501 | static 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 | ||
1512 | static inline u32 nfsd4_remove_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op) | 1513 | static 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 | ||
2577 | static __be32 | 2577 | static __be32 |
2578 | nfsd4_encode_dirent_fattr(struct nfsd4_readdir *cd, | 2578 | nfsd4_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 | } |
2630 | out_encode: | 2630 | out_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); |
2634 | out_put: | 2633 | out_put: |
2635 | dput(dentry); | 2634 | dput(dentry); |
@@ -2638,9 +2637,12 @@ out_put: | |||
2638 | } | 2637 | } |
2639 | 2638 | ||
2640 | static __be32 * | 2639 | static __be32 * |
2641 | nfsd4_encode_rdattr_error(__be32 *p, int buflen, __be32 nfserr) | 2640 | nfsd4_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; | ||
2711 | skip_entry: | 2724 | skip_entry: |
2712 | cd->common.err = nfs_ok; | 2725 | cd->common.err = nfs_ok; |
2713 | return 0; | 2726 | return 0; |
2714 | fail: | 2727 | fail: |
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 | |||
3206 | nfsd4_encode_readdir(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_readdir *readdir) | 3220 | nfsd4_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; |
3290 | err_no_verf: | 3303 | err_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 | ||
295 | struct nfsd4_release_lockowner { | 294 | struct nfsd4_release_lockowner { |