1 // Copyright 2008 Google Inc. 2 // Author: Lincoln Smith 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 16 #include <config.h> 17 #include "headerparser.h" 18 #include "logging.h" 19 #include "varint_bigendian.h" 20 #include "vcdiff_defs.h" 21 22 namespace open_vcdiff { 23 24 // *** Methods for ParseableChunk 25 26 void ParseableChunk::Advance(size_t number_of_bytes) { 27 if (number_of_bytes > UnparsedSize()) { 28 LOG(DFATAL) << "Internal error: position advanced by " << number_of_bytes 29 << " bytes, current unparsed size " << UnparsedSize() 30 << LOG_ENDL; 31 position_ = end_; 32 return; 33 } 34 position_ += number_of_bytes; 35 } 36 37 void ParseableChunk::SetPosition(const char* position) { 38 if (position < start_) { 39 LOG(DFATAL) << "Internal error: new data position " << position 40 << " is beyond start of data " << start_ << LOG_ENDL; 41 position_ = start_; 42 return; 43 } 44 if (position > end_) { 45 LOG(DFATAL) << "Internal error: new data position " << position 46 << " is beyond end of data " << end_ << LOG_ENDL; 47 position_ = end_; 48 return; 49 } 50 position_ = position; 51 } 52 53 void ParseableChunk::FinishExcept(size_t number_of_bytes) { 54 if (number_of_bytes > UnparsedSize()) { 55 LOG(DFATAL) << "Internal error: specified number of remaining bytes " 56 << number_of_bytes << " is greater than unparsed data size " 57 << UnparsedSize() << LOG_ENDL; 58 Finish(); 59 return; 60 } 61 position_ = end_ - number_of_bytes; 62 } 63 64 // *** Methods for VCDiffHeaderParser 65 66 VCDiffHeaderParser::VCDiffHeaderParser(const char* header_start, 67 const char* data_end) 68 : parseable_chunk_(header_start, data_end - header_start), 69 return_code_(RESULT_SUCCESS), 70 delta_encoding_length_(0), 71 delta_encoding_start_(NULL) { } 72 73 bool VCDiffHeaderParser::ParseByte(unsigned char* value) { 74 if (RESULT_SUCCESS != return_code_) { 75 return false; 76 } 77 if (parseable_chunk_.Empty()) { 78 return_code_ = RESULT_END_OF_DATA; 79 return false; 80 } 81 *value = static_cast<unsigned char>(*parseable_chunk_.UnparsedData()); 82 parseable_chunk_.Advance(1); 83 return true; 84 } 85 86 bool VCDiffHeaderParser::ParseInt32(const char* variable_description, 87 int32_t* value) { 88 if (RESULT_SUCCESS != return_code_) { 89 return false; 90 } 91 int32_t parsed_value = 92 VarintBE<int32_t>::Parse(parseable_chunk_.End(), 93 parseable_chunk_.UnparsedDataAddr()); 94 switch (parsed_value) { 95 case RESULT_ERROR: 96 LOG(ERROR) << "Expected " << variable_description 97 << "; found invalid variable-length integer" << LOG_ENDL; 98 return_code_ = RESULT_ERROR; 99 return false; 100 case RESULT_END_OF_DATA: 101 return_code_ = RESULT_END_OF_DATA; 102 return false; 103 default: 104 *value = parsed_value; 105 return true; 106 } 107 } 108 109 // When an unsigned 32-bit integer is expected, parse a signed 64-bit value 110 // instead, then check the value limit. The uint32_t type can't be parsed 111 // directly because two negative values are given special meanings (RESULT_ERROR 112 // and RESULT_END_OF_DATA) and could not be expressed in an unsigned format. 113 bool VCDiffHeaderParser::ParseUInt32(const char* variable_description, 114 uint32_t* value) { 115 if (RESULT_SUCCESS != return_code_) { 116 return false; 117 } 118 int64_t parsed_value = 119 VarintBE<int64_t>::Parse(parseable_chunk_.End(), 120 parseable_chunk_.UnparsedDataAddr()); 121 switch (parsed_value) { 122 case RESULT_ERROR: 123 LOG(ERROR) << "Expected " << variable_description 124 << "; found invalid variable-length integer" << LOG_ENDL; 125 return_code_ = RESULT_ERROR; 126 return false; 127 case RESULT_END_OF_DATA: 128 return_code_ = RESULT_END_OF_DATA; 129 return false; 130 default: 131 if (parsed_value > 0xFFFFFFFF) { 132 LOG(ERROR) << "Value of " << variable_description << "(" << parsed_value 133 << ") is too large for unsigned 32-bit integer" << LOG_ENDL; 134 return_code_ = RESULT_ERROR; 135 return false; 136 } 137 *value = static_cast<uint32_t>(parsed_value); 138 return true; 139 } 140 } 141 142 // A VCDChecksum represents an unsigned 32-bit value returned by adler32(), 143 // but isn't a uint32_t. 144 bool VCDiffHeaderParser::ParseChecksum(const char* variable_description, 145 VCDChecksum* value) { 146 uint32_t parsed_value = 0; 147 if (!ParseUInt32(variable_description, &parsed_value)) { 148 return false; 149 } 150 *value = static_cast<VCDChecksum>(parsed_value); 151 return true; 152 } 153 154 bool VCDiffHeaderParser::ParseSize(const char* variable_description, 155 size_t* value) { 156 int32_t parsed_value = 0; 157 if (!ParseInt32(variable_description, &parsed_value)) { 158 return false; 159 } 160 *value = static_cast<size_t>(parsed_value); 161 return true; 162 } 163 164 bool VCDiffHeaderParser::ParseSourceSegmentLengthAndPosition( 165 size_t from_size, 166 const char* from_boundary_name, 167 const char* from_name, 168 size_t* source_segment_length, 169 size_t* source_segment_position) { 170 // Verify the length and position values 171 if (!ParseSize("source segment length", source_segment_length)) { 172 return false; 173 } 174 // Guard against overflow by checking source length first 175 if (*source_segment_length > from_size) { 176 LOG(ERROR) << "Source segment length (" << *source_segment_length 177 << ") is larger than " << from_name << " (" << from_size 178 << ")" << LOG_ENDL; 179 return_code_ = RESULT_ERROR; 180 return false; 181 } 182 if (!ParseSize("source segment position", source_segment_position)) { 183 return false; 184 } 185 if ((*source_segment_position >= from_size) && 186 (*source_segment_length > 0)) { 187 LOG(ERROR) << "Source segment position (" << *source_segment_position 188 << ") is past " << from_boundary_name 189 << " (" << from_size << ")" << LOG_ENDL; 190 return_code_ = RESULT_ERROR; 191 return false; 192 } 193 const size_t source_segment_end = *source_segment_position + 194 *source_segment_length; 195 if (source_segment_end > from_size) { 196 LOG(ERROR) << "Source segment end position (" << source_segment_end 197 << ") is past " << from_boundary_name 198 << " (" << from_size << ")" << LOG_ENDL; 199 return_code_ = RESULT_ERROR; 200 return false; 201 } 202 return true; 203 } 204 205 bool VCDiffHeaderParser::ParseWinIndicatorAndSourceSegment( 206 size_t dictionary_size, 207 size_t decoded_target_size, 208 bool allow_vcd_target, 209 unsigned char* win_indicator, 210 size_t* source_segment_length, 211 size_t* source_segment_position) { 212 if (!ParseByte(win_indicator)) { 213 return false; 214 } 215 unsigned char source_target_flags = 216 *win_indicator & (VCD_SOURCE | VCD_TARGET); 217 switch (source_target_flags) { 218 case VCD_SOURCE: 219 return ParseSourceSegmentLengthAndPosition(dictionary_size, 220 "end of dictionary", 221 "dictionary", 222 source_segment_length, 223 source_segment_position); 224 case VCD_TARGET: 225 if (!allow_vcd_target) { 226 LOG(ERROR) << "Delta file contains VCD_TARGET flag, which is not " 227 "allowed by current decoder settings" << LOG_ENDL; 228 return_code_ = RESULT_ERROR; 229 return false; 230 } 231 return ParseSourceSegmentLengthAndPosition(decoded_target_size, 232 "current target position", 233 "target file", 234 source_segment_length, 235 source_segment_position); 236 case VCD_SOURCE | VCD_TARGET: 237 LOG(ERROR) << "Win_Indicator must not have both VCD_SOURCE" 238 " and VCD_TARGET set" << LOG_ENDL; 239 return_code_ = RESULT_ERROR; 240 return false; 241 default: 242 return true; 243 } 244 } 245 246 bool VCDiffHeaderParser::ParseWindowLengths(size_t* target_window_length) { 247 if (delta_encoding_start_) { 248 LOG(DFATAL) << "Internal error: VCDiffHeaderParser::ParseWindowLengths " 249 "was called twice for the same delta window" << LOG_ENDL; 250 return_code_ = RESULT_ERROR; 251 return false; 252 } 253 if (!ParseSize("length of the delta encoding", &delta_encoding_length_)) { 254 return false; 255 } 256 delta_encoding_start_ = UnparsedData(); 257 if (!ParseSize("size of the target window", target_window_length)) { 258 return false; 259 } 260 return true; 261 } 262 263 const char* VCDiffHeaderParser::EndOfDeltaWindow() const { 264 if (!delta_encoding_start_) { 265 LOG(DFATAL) << "Internal error: VCDiffHeaderParser::GetDeltaWindowEnd " 266 "was called before ParseWindowLengths" << LOG_ENDL; 267 return NULL; 268 } 269 return delta_encoding_start_ + delta_encoding_length_; 270 } 271 272 bool VCDiffHeaderParser::ParseDeltaIndicator() { 273 unsigned char delta_indicator; 274 if (!ParseByte(&delta_indicator)) { 275 return false; 276 } 277 if (delta_indicator & (VCD_DATACOMP | VCD_INSTCOMP | VCD_ADDRCOMP)) { 278 LOG(ERROR) << "Secondary compression of delta file sections " 279 "is not supported" << LOG_ENDL; 280 return_code_ = RESULT_ERROR; 281 return false; 282 } 283 return true; 284 } 285 286 bool VCDiffHeaderParser::ParseSectionLengths( 287 bool has_checksum, 288 size_t* add_and_run_data_length, 289 size_t* instructions_and_sizes_length, 290 size_t* addresses_length, 291 VCDChecksum* checksum) { 292 ParseSize("length of data for ADDs and RUNs", add_and_run_data_length); 293 ParseSize("length of instructions section", instructions_and_sizes_length); 294 ParseSize("length of addresses for COPYs", addresses_length); 295 if (has_checksum) { 296 ParseChecksum("Adler32 checksum value", checksum); 297 } 298 if (RESULT_SUCCESS != return_code_) { 299 return false; 300 } 301 if (!delta_encoding_start_) { 302 LOG(DFATAL) << "Internal error: VCDiffHeaderParser::ParseSectionLengths " 303 "was called before ParseWindowLengths" << LOG_ENDL; 304 return_code_ = RESULT_ERROR; 305 return false; 306 } 307 const size_t delta_encoding_header_length = 308 UnparsedData() - delta_encoding_start_; 309 if (delta_encoding_length_ != 310 (delta_encoding_header_length + 311 *add_and_run_data_length + 312 *instructions_and_sizes_length + 313 *addresses_length)) { 314 LOG(ERROR) << "The length of the delta encoding does not match " 315 "the size of the header plus the sizes of the data sections" 316 << LOG_ENDL; 317 return_code_ = RESULT_ERROR; 318 return false; 319 } 320 return true; 321 } 322 323 } // namespace open_vcdiff 324