@@ -1025,82 +1025,95 @@ static int decode_deallocate(struct xdr_stream *xdr, struct nfs42_falloc_res *re
1025
1025
return decode_op_hdr (xdr , OP_DEALLOCATE );
1026
1026
}
1027
1027
1028
- static int decode_read_plus_data (struct xdr_stream * xdr ,
1029
- struct nfs_pgio_args * args ,
1030
- struct nfs_pgio_res * res )
1031
- {
1032
- uint32_t count , recvd ;
1028
+ struct read_plus_segment {
1029
+ enum data_content4 type ;
1033
1030
uint64_t offset ;
1034
- __be32 * p ;
1035
-
1036
- p = xdr_inline_decode (xdr , 8 + 4 );
1037
- if (!p )
1038
- return 1 ;
1031
+ union {
1032
+ struct {
1033
+ uint64_t length ;
1034
+ } hole ;
1035
+
1036
+ struct {
1037
+ uint32_t length ;
1038
+ unsigned int from ;
1039
+ } data ;
1040
+ };
1041
+ };
1039
1042
1040
- p = xdr_decode_hyper (p , & offset );
1041
- count = be32_to_cpup (p );
1042
- recvd = xdr_align_data (xdr , res -> count , xdr_align_size (count ));
1043
- if (recvd > count )
1044
- recvd = count ;
1045
- if (res -> count + recvd > args -> count ) {
1046
- if (args -> count > res -> count )
1047
- res -> count += args -> count - res -> count ;
1048
- return 1 ;
1049
- }
1050
- res -> count += recvd ;
1051
- if (count > recvd )
1052
- return 1 ;
1053
- return 0 ;
1043
+ static inline uint64_t read_plus_segment_length (struct read_plus_segment * seg )
1044
+ {
1045
+ return seg -> type == NFS4_CONTENT_DATA ? seg -> data .length : seg -> hole .length ;
1054
1046
}
1055
1047
1056
- static int decode_read_plus_hole (struct xdr_stream * xdr ,
1057
- struct nfs_pgio_args * args ,
1058
- struct nfs_pgio_res * res , uint32_t * eof )
1048
+ static int decode_read_plus_segment (struct xdr_stream * xdr ,
1049
+ struct read_plus_segment * seg )
1059
1050
{
1060
- uint64_t offset , length , recvd ;
1061
1051
__be32 * p ;
1062
1052
1063
- p = xdr_inline_decode (xdr , 8 + 8 );
1053
+ p = xdr_inline_decode (xdr , 4 );
1064
1054
if (!p )
1065
- return 1 ;
1066
-
1067
- p = xdr_decode_hyper (p , & offset );
1068
- p = xdr_decode_hyper (p , & length );
1069
- if (offset != args -> offset + res -> count ) {
1070
- /* Server returned an out-of-sequence extent */
1071
- if (offset > args -> offset + res -> count ||
1072
- offset + length < args -> offset + res -> count ) {
1073
- dprintk ("NFS: server returned out of sequence extent: "
1074
- "offset/size = %llu/%llu != expected %llu\n" ,
1075
- (unsigned long long )offset ,
1076
- (unsigned long long )length ,
1077
- (unsigned long long )(args -> offset +
1078
- res -> count ));
1079
- return 1 ;
1080
- }
1081
- length -= args -> offset + res -> count - offset ;
1082
- }
1083
- if (length + res -> count > args -> count ) {
1084
- * eof = 0 ;
1085
- if (unlikely (res -> count >= args -> count ))
1086
- return 1 ;
1087
- length = args -> count - res -> count ;
1088
- }
1089
- recvd = xdr_expand_hole (xdr , res -> count , length );
1090
- res -> count += recvd ;
1055
+ return - EIO ;
1056
+ seg -> type = be32_to_cpup (p ++ );
1057
+
1058
+ p = xdr_inline_decode (xdr , seg -> type == NFS4_CONTENT_DATA ? 12 : 16 );
1059
+ if (!p )
1060
+ return - EIO ;
1061
+ p = xdr_decode_hyper (p , & seg -> offset );
1062
+
1063
+ if (seg -> type == NFS4_CONTENT_DATA ) {
1064
+ struct xdr_buf buf ;
1065
+ uint32_t len = be32_to_cpup (p );
1066
+
1067
+ seg -> data .length = len ;
1068
+ seg -> data .from = xdr_stream_pos (xdr );
1091
1069
1092
- if (recvd < length )
1093
- return 1 ;
1070
+ if (!xdr_stream_subsegment (xdr , & buf , xdr_align_size (len )))
1071
+ return - EIO ;
1072
+ } else if (seg -> type == NFS4_CONTENT_HOLE ) {
1073
+ xdr_decode_hyper (p , & seg -> hole .length );
1074
+ } else
1075
+ return - EINVAL ;
1094
1076
return 0 ;
1095
1077
}
1096
1078
1079
+ static int process_read_plus_segment (struct xdr_stream * xdr ,
1080
+ struct nfs_pgio_args * args ,
1081
+ struct nfs_pgio_res * res ,
1082
+ struct read_plus_segment * seg )
1083
+ {
1084
+ unsigned long offset = seg -> offset ;
1085
+ unsigned long length = read_plus_segment_length (seg );
1086
+ unsigned int bufpos ;
1087
+
1088
+ if (offset + length < args -> offset )
1089
+ return 0 ;
1090
+ else if (offset > args -> offset + args -> count ) {
1091
+ res -> eof = 0 ;
1092
+ return 0 ;
1093
+ } else if (offset < args -> offset ) {
1094
+ length -= (args -> offset - offset );
1095
+ offset = args -> offset ;
1096
+ } else if (offset + length > args -> offset + args -> count ) {
1097
+ length = (args -> offset + args -> count ) - offset ;
1098
+ res -> eof = 0 ;
1099
+ }
1100
+
1101
+ bufpos = xdr -> buf -> head [0 ].iov_len + (offset - args -> offset );
1102
+ if (seg -> type == NFS4_CONTENT_HOLE )
1103
+ return xdr_stream_zero (xdr , bufpos , length );
1104
+ else
1105
+ return xdr_stream_move_subsegment (xdr , seg -> data .from , bufpos , length );
1106
+ }
1107
+
1097
1108
static int decode_read_plus (struct xdr_stream * xdr , struct nfs_pgio_res * res )
1098
1109
{
1099
1110
struct nfs_pgio_header * hdr =
1100
1111
container_of (res , struct nfs_pgio_header , res );
1101
1112
struct nfs_pgio_args * args = & hdr -> args ;
1102
- uint32_t eof , segments , type ;
1113
+ uint32_t segments ;
1114
+ struct read_plus_segment * segs ;
1103
1115
int status , i ;
1116
+ char scratch_buf [16 ];
1104
1117
__be32 * p ;
1105
1118
1106
1119
status = decode_op_hdr (xdr , OP_READ_PLUS );
@@ -1112,38 +1125,31 @@ static int decode_read_plus(struct xdr_stream *xdr, struct nfs_pgio_res *res)
1112
1125
return - EIO ;
1113
1126
1114
1127
res -> count = 0 ;
1115
- eof = be32_to_cpup (p ++ );
1128
+ res -> eof = be32_to_cpup (p ++ );
1116
1129
segments = be32_to_cpup (p ++ );
1117
1130
if (segments == 0 )
1118
- goto out ;
1119
-
1120
- for (i = 0 ; i < segments ; i ++ ) {
1121
- p = xdr_inline_decode (xdr , 4 );
1122
- if (!p )
1123
- goto early_out ;
1131
+ return status ;
1124
1132
1125
- type = be32_to_cpup (p ++ );
1126
- if (type == NFS4_CONTENT_DATA )
1127
- status = decode_read_plus_data (xdr , args , res );
1128
- else if (type == NFS4_CONTENT_HOLE )
1129
- status = decode_read_plus_hole (xdr , args , res , & eof );
1130
- else
1131
- return - EINVAL ;
1133
+ segs = kmalloc_array (segments , sizeof (* segs ), GFP_KERNEL );
1134
+ if (!segs )
1135
+ return - ENOMEM ;
1132
1136
1137
+ xdr_set_scratch_buffer (xdr , & scratch_buf , 32 );
1138
+ status = - EIO ;
1139
+ for (i = 0 ; i < segments ; i ++ ) {
1140
+ status = decode_read_plus_segment (xdr , & segs [i ]);
1133
1141
if (status < 0 )
1134
- return status ;
1135
- if (status > 0 )
1136
- goto early_out ;
1142
+ goto out ;
1137
1143
}
1138
1144
1145
+ xdr_set_pagelen (xdr , xdr_align_size (args -> count ));
1146
+ for (i = segments ; i > 0 ; i -- )
1147
+ res -> count += process_read_plus_segment (xdr , args , res , & segs [i - 1 ]);
1148
+ status = 0 ;
1149
+
1139
1150
out :
1140
- res -> eof = eof ;
1141
- return 0 ;
1142
- early_out :
1143
- if (unlikely (!i ))
1144
- return - EIO ;
1145
- res -> eof = 0 ;
1146
- return 0 ;
1151
+ kfree (segs );
1152
+ return status ;
1147
1153
}
1148
1154
1149
1155
static int decode_seek (struct xdr_stream * xdr , struct nfs42_seek_res * res )
0 commit comments