@@ -2575,8 +2575,8 @@ static inline int attributes_need_mount(u32 *bmval)
2575
2575
}
2576
2576
2577
2577
static __be32
2578
- nfsd4_encode_dirent_fattr (struct nfsd4_readdir * cd ,
2579
- const char * name , int namlen , __be32 * * p , int buflen )
2578
+ nfsd4_encode_dirent_fattr (struct xdr_stream * xdr , struct nfsd4_readdir * cd ,
2579
+ const char * name , int namlen )
2580
2580
{
2581
2581
struct svc_export * exp = cd -> rd_fhp -> fh_export ;
2582
2582
struct dentry * dentry ;
@@ -2628,8 +2628,7 @@ nfsd4_encode_dirent_fattr(struct nfsd4_readdir *cd,
2628
2628
2629
2629
}
2630
2630
out_encode :
2631
- nfserr = nfsd4_encode_fattr_to_buf (p , buflen , NULL , exp , dentry ,
2632
- cd -> rd_bmval ,
2631
+ nfserr = nfsd4_encode_fattr (xdr , NULL , exp , dentry , cd -> rd_bmval ,
2633
2632
cd -> rd_rqstp , ignore_crossmnt );
2634
2633
out_put :
2635
2634
dput (dentry );
@@ -2638,9 +2637,12 @@ nfsd4_encode_dirent_fattr(struct nfsd4_readdir *cd,
2638
2637
}
2639
2638
2640
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
2646
return NULL ;
2645
2647
* p ++ = htonl (2 );
2646
2648
* p ++ = htonl (FATTR4_WORD0_RDATTR_ERROR ); /* bmval0 */
@@ -2657,30 +2659,38 @@ nfsd4_encode_dirent(void *ccdv, const char *name, int namlen,
2657
2659
{
2658
2660
struct readdir_cd * ccd = ccdv ;
2659
2661
struct nfsd4_readdir * cd = container_of (ccd , struct nfsd4_readdir , common );
2660
- int buflen ;
2661
- __be32 * p = cd -> buffer ;
2662
- __be32 * cookiep ;
2662
+ struct xdr_stream * xdr = cd -> xdr ;
2663
+ int start_offset = xdr -> buf -> len ;
2664
+ int cookie_offset ;
2665
+ int entry_bytes ;
2663
2666
__be32 nfserr = nfserr_toosmall ;
2667
+ __be64 wire_offset ;
2668
+ __be32 * p ;
2664
2669
2665
2670
/* In nfsv4, "." and ".." never make it onto the wire.. */
2666
2671
if (name && isdotent (name , namlen )) {
2667
2672
cd -> common .err = nfs_ok ;
2668
2673
return 0 ;
2669
2674
}
2670
2675
2671
- if (cd -> offset )
2672
- xdr_encode_hyper (cd -> offset , (u64 ) offset );
2676
+ if (cd -> cookie_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 );
2675
- if (buflen < 0 )
2682
+ p = xdr_reserve_space ( xdr , 4 );
2683
+ if (! p )
2676
2684
goto fail ;
2677
-
2678
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
2690
p = xdr_encode_hyper (p , NFS_OFFSET_MAX ); /* offset of next entry */
2681
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
2694
switch (nfserr ) {
2685
2695
case nfs_ok :
2686
2696
break ;
@@ -2699,19 +2709,23 @@ nfsd4_encode_dirent(void *ccdv, const char *name, int namlen,
2699
2709
*/
2700
2710
if (!(cd -> rd_bmval [0 ] & FATTR4_WORD0_RDATTR_ERROR ))
2701
2711
goto fail ;
2702
- p = nfsd4_encode_rdattr_error (p , buflen , nfserr );
2712
+ p = nfsd4_encode_rdattr_error (xdr , nfserr );
2703
2713
if (p == NULL ) {
2704
2714
nfserr = nfserr_toosmall ;
2705
2715
goto fail ;
2706
2716
}
2707
2717
}
2708
- cd -> buflen -= (p - cd -> buffer );
2709
- cd -> buffer = p ;
2710
- cd -> offset = cookiep ;
2718
+ nfserr = nfserr_toosmall ;
2719
+ entry_bytes = xdr -> buf -> len - start_offset ;
2720
+ if (entry_bytes > cd -> rd_maxcount )
2721
+ goto fail ;
2722
+ cd -> rd_maxcount -= entry_bytes ;
2723
+ cd -> cookie_offset = cookie_offset ;
2711
2724
skip_entry :
2712
2725
cd -> common .err = nfs_ok ;
2713
2726
return 0 ;
2714
2727
fail :
2728
+ xdr_truncate_encode (xdr , start_offset );
2715
2729
cd -> common .err = nfserr ;
2716
2730
return - EINVAL ;
2717
2731
}
@@ -3206,10 +3220,11 @@ static __be32
3206
3220
nfsd4_encode_readdir (struct nfsd4_compoundres * resp , __be32 nfserr , struct nfsd4_readdir * readdir )
3207
3221
{
3208
3222
int maxcount ;
3223
+ int bytes_left ;
3209
3224
loff_t offset ;
3225
+ __be64 wire_offset ;
3210
3226
struct xdr_stream * xdr = & resp -> xdr ;
3211
3227
int starting_len = xdr -> buf -> len ;
3212
- __be32 * page , * tailbase ;
3213
3228
__be32 * p ;
3214
3229
3215
3230
if (nfserr )
@@ -3219,72 +3234,70 @@ nfsd4_encode_readdir(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4
3219
3234
if (!p )
3220
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
3237
/* XXX: Following NFSv3, we ignore the READDIR verifier for now. */
3228
3238
WRITE32 (0 );
3229
3239
WRITE32 (0 );
3230
3240
resp -> xdr .buf -> head [0 ].iov_len = ((char * )resp -> xdr .p )
3231
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
3240
- * written, make sure to leave two words at the end for the next
3241
- * pointer and eof field.
3244
+ * Number of bytes left for directory entries allowing for the
3245
+ * final 8 bytes of the readdir and a following failed op:
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 ;
3244
- if (maxcount < 0 ) {
3245
- nfserr = nfserr_toosmall ;
3259
+ if (maxcount < 16 ) {
3260
+ nfserr = nfserr_toosmall ;
3246
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
3267
readdir -> common .err = 0 ;
3251
- readdir -> buflen = maxcount ;
3252
- readdir -> buffer = page ;
3253
- readdir -> offset = NULL ;
3268
+ readdir -> cookie_offset = 0 ;
3254
3269
3255
3270
offset = readdir -> rd_cookie ;
3256
3271
nfserr = nfsd_readdir (readdir -> rd_rqstp , readdir -> rd_fhp ,
3257
3272
& offset ,
3258
3273
& readdir -> common , nfsd4_encode_dirent );
3259
3274
if (nfserr == nfs_ok &&
3260
3275
readdir -> common .err == nfserr_toosmall &&
3261
- readdir -> buffer == page )
3262
- nfserr = nfserr_toosmall ;
3276
+ xdr -> buf -> len == starting_len + 8 ) {
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
3285
if (nfserr )
3264
3286
goto err_no_verf ;
3265
3287
3266
- if (readdir -> offset )
3267
- xdr_encode_hyper (readdir -> offset , offset );
3288
+ if (readdir -> cookie_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
3299
* p ++ = 0 ; /* no more entries */
3271
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
3302
return 0 ;
3290
3303
err_no_verf :
0 commit comments