| 398 | // Read and parse the protobuf container file-level header documented in pb_util.h. |
| 399 | template<typename ReadableFileType> |
| 400 | Status ParsePBFileHeader(ReadableFileType* reader, optional<uint64_t>* cached_file_size, |
| 401 | uint64_t* offset, int* version) { |
| 402 | RETURN_NOT_OK(CacheFileSize(reader, cached_file_size)); |
| 403 | const uint64_t file_size = *(*cached_file_size); |
| 404 | |
| 405 | // We initially read enough data for a V2+ file header. This optimizes for |
| 406 | // V2+ and is valid on a V1 file because we don't consider these files valid |
| 407 | // unless they contain a record in addition to the file header. The |
| 408 | // additional 4 bytes required by a V2+ header (vs V1) is still less than the |
| 409 | // minimum number of bytes required for a V1 format data record. |
| 410 | uint64_t tmp_offset = *offset; |
| 411 | faststring header; |
| 412 | RETURN_NOT_OK_PREPEND(ValidateAndReadData(reader, file_size, &tmp_offset, kPBContainerV2HeaderLen, |
| 413 | &header), |
| 414 | Substitute("Could not read header for proto container file $0", |
| 415 | reader->filename())); |
| 416 | Slice magic_and_version(header.data(), kPBContainerMagicLen + sizeof(uint32_t)); |
| 417 | Slice checksum(header.data() + kPBContainerMagicLen + sizeof(uint32_t), kPBContainerChecksumLen); |
| 418 | |
| 419 | // Validate magic number. |
| 420 | if (PREDICT_FALSE(!strings::memeq(kPBContainerMagic, header.data(), kPBContainerMagicLen))) { |
| 421 | string file_magic(reinterpret_cast<const char*>(header.data()), kPBContainerMagicLen); |
| 422 | return Status::Corruption("Invalid magic number", |
| 423 | Substitute("Expected: $0, found: $1", |
| 424 | Utf8SafeCEscape(kPBContainerMagic), |
| 425 | Utf8SafeCEscape(file_magic))); |
| 426 | } |
| 427 | |
| 428 | // Validate container file version. |
| 429 | uint32_t tmp_version = DecodeFixed32(header.data() + kPBContainerMagicLen); |
| 430 | if (PREDICT_FALSE(!IsSupportedContainerVersion(tmp_version))) { |
| 431 | return Status::NotSupported( |
| 432 | Substitute("Protobuf container has unsupported version: $0. Default version: $1", |
| 433 | tmp_version, kPBContainerDefaultVersion)); |
| 434 | } |
| 435 | |
| 436 | // Versions >= 2 have a checksum after the magic number and encoded version |
| 437 | // to ensure the integrity of these fields. |
| 438 | if (tmp_version >= 2) { |
| 439 | RETURN_NOT_OK_PREPEND(ParseAndCompareChecksum(checksum.data(), { magic_and_version }), |
| 440 | CHECKSUM_ERR_MSG("File header checksum does not match", |
| 441 | reader->filename(), tmp_offset - kPBContainerChecksumLen)); |
| 442 | } else { |
| 443 | // Version 1 doesn't have a header checksum. Rewind our read offset so this |
| 444 | // data will be read again when we next attempt to read a data record. |
| 445 | tmp_offset -= kPBContainerChecksumLen; |
| 446 | } |
| 447 | |
| 448 | *offset = tmp_offset; |
| 449 | *version = tmp_version; |
| 450 | return Status::OK(); |
| 451 | } |
| 452 | |
| 453 | // Read and parse the supplemental header from the container file. |
| 454 | template<typename ReadableFileType> |
no test coverage detected