1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/utility/safe_browsing/mac/udif.h"
8 #include <CoreFoundation/CoreFoundation.h>
9 #include <libkern/OSByteOrder.h>
10 #include <uuid/uuid.h>
14 #include "base/logging.h"
15 #include "base/mac/foundation_util.h"
16 #include "base/mac/scoped_cftyperef.h"
17 #include "base/numerics/safe_math.h"
18 #include "base/strings/sys_string_conversions.h"
19 #include "chrome/utility/safe_browsing/mac/convert_big_endian.h"
20 #include "chrome/utility/safe_browsing/mac/read_stream.h"
21 #include "third_party/zlib/zlib.h"
23 namespace safe_browsing
{
28 // The following structures come from the analysis provided by Jonathan Levin
29 // at <http://newosxbook.com/DMG.html>.
31 // Note that all fields are stored in big endian.
39 static void ConvertBigEndian(UDIFChecksum
* checksum
) {
40 ConvertBigEndian(&checksum
->type
);
41 ConvertBigEndian(&checksum
->size
);
42 for (size_t i
= 0; i
< arraysize(checksum
->data
); ++i
) {
43 ConvertBigEndian(&checksum
->data
[i
]);
47 // The trailer structure for a UDIF file.
48 struct UDIFResourceFile
{
49 static const uint32_t kSignature
= 'koly';
50 static const uint32_t kVersion
= 4;
54 uint32_t header_size
; // Size of this structure.
56 uint64_t running_data_fork_offset
;
57 uint64_t data_fork_offset
;
58 uint64_t data_fork_length
;
59 uint64_t rsrc_fork_offset
;
60 uint64_t rsrc_fork_length
;
61 uint32_t segment_number
;
62 uint32_t segment_count
;
65 UDIFChecksum data_checksum
;
67 uint64_t plist_offset
; // Offset and length of the blkx plist.
68 uint64_t plist_length
;
70 uint8_t reserved1
[120];
72 UDIFChecksum master_checksum
;
74 uint32_t image_variant
;
75 uint64_t sector_count
;
82 static void ConvertBigEndian(uuid_t
* uuid
) {
83 // UUID is never consulted, so do not swap.
86 static void ConvertBigEndian(UDIFResourceFile
* file
) {
87 ConvertBigEndian(&file
->signature
);
88 ConvertBigEndian(&file
->version
);
89 ConvertBigEndian(&file
->flags
);
90 ConvertBigEndian(&file
->header_size
);
91 ConvertBigEndian(&file
->running_data_fork_offset
);
92 ConvertBigEndian(&file
->data_fork_offset
);
93 ConvertBigEndian(&file
->data_fork_length
);
94 ConvertBigEndian(&file
->rsrc_fork_offset
);
95 ConvertBigEndian(&file
->rsrc_fork_length
);
96 ConvertBigEndian(&file
->segment_number
);
97 ConvertBigEndian(&file
->segment_count
);
98 ConvertBigEndian(&file
->segment_id
);
99 ConvertBigEndian(&file
->data_checksum
);
100 ConvertBigEndian(&file
->plist_offset
);
101 ConvertBigEndian(&file
->plist_length
);
102 ConvertBigEndian(&file
->master_checksum
);
103 ConvertBigEndian(&file
->image_variant
);
104 ConvertBigEndian(&file
->sector_count
);
105 // Reserved fields are skipped.
108 struct UDIFBlockChunk
{
109 enum class Type
: uint32_t {
110 ZERO_FILL
= 0x00000000,
111 UNCOMPRESSED
= 0x00000001,
112 IGNORED
= 0x00000002,
113 COMPRESS_ADC
= 0x80000004,
114 COMPRESS_ZLIB
= 0x80000005,
115 COMPRESSS_BZ2
= 0x80000006,
116 COMMENT
= 0x7ffffffe,
117 LAST_BLOCK
= 0xffffffff,
122 uint64_t start_sector
; // Logical chunk offset and length, in sectors.
123 uint64_t sector_count
;
124 uint64_t compressed_offset
; // Compressed offset and length, in bytes.
125 uint64_t compressed_length
;
128 static void ConvertBigEndian(UDIFBlockChunk
* chunk
) {
129 ConvertBigEndian(reinterpret_cast<uint32_t*>(&chunk
->type
));
130 ConvertBigEndian(&chunk
->comment
);
131 ConvertBigEndian(&chunk
->start_sector
);
132 ConvertBigEndian(&chunk
->sector_count
);
133 ConvertBigEndian(&chunk
->compressed_offset
);
134 ConvertBigEndian(&chunk
->compressed_length
);
137 struct UDIFBlockData
{
138 static const uint32_t kSignature
= 'mish';
139 static const uint32_t kVersion
= 1;
143 uint64_t start_sector
; // Logical block offset and length, in sectors.
144 uint64_t sector_count
;
146 uint64_t data_offset
;
147 uint32_t buffers_needed
;
148 uint32_t block_descriptors
;
157 UDIFChecksum checksum
;
159 uint32_t chunk_count
;
160 UDIFBlockChunk chunks
[0];
163 static void ConvertBigEndian(UDIFBlockData
* block
) {
164 ConvertBigEndian(&block
->signature
);
165 ConvertBigEndian(&block
->version
);
166 ConvertBigEndian(&block
->start_sector
);
167 ConvertBigEndian(&block
->sector_count
);
168 ConvertBigEndian(&block
->data_offset
);
169 ConvertBigEndian(&block
->buffers_needed
);
170 ConvertBigEndian(&block
->block_descriptors
);
171 // Reserved fields are skipped.
172 ConvertBigEndian(&block
->checksum
);
173 ConvertBigEndian(&block
->chunk_count
);
174 // Note: This deliberately does not swap the chunks themselves.
177 // UDIFBlock takes a raw, big-endian block data pointer and stores, in host
178 // endian, the data for both the block and the chunk.
181 explicit UDIFBlock(const UDIFBlockData
* block_data
) : block(*block_data
) {
182 ConvertBigEndian(&block
);
183 for (uint32_t i
= 0; i
< block
.chunk_count
; ++i
) {
184 chunks
.push_back(block_data
->chunks
[i
]);
185 ConvertBigEndian(&chunks
[i
]);
189 uint32_t signature() const { return block
.signature
; }
190 uint32_t version() const { return block
.version
; }
191 uint64_t start_sector() const { return block
.start_sector
; }
192 uint64_t sector_count() const { return block
.sector_count
; }
193 uint64_t chunk_count() const { return chunks
.size(); }
195 const UDIFBlockChunk
* chunk(uint32_t i
) const {
196 if (i
>= chunk_count())
203 std::vector
<UDIFBlockChunk
> chunks
;
205 DISALLOW_COPY_AND_ASSIGN(UDIFBlock
);
212 const size_t kSectorSize
= 512;
214 class UDIFBlockChunkReadStream
;
216 // A UDIFPartitionReadStream virtualizes a partition's non-contiguous blocks
217 // into a single stream.
218 class UDIFPartitionReadStream
: public ReadStream
{
220 UDIFPartitionReadStream(ReadStream
* stream
,
222 const UDIFBlock
* partition_block
);
223 ~UDIFPartitionReadStream() override
;
225 bool Read(uint8_t* buffer
, size_t buffer_size
, size_t* bytes_read
) override
;
226 // Seek only supports SEEK_SET and SEEK_CUR.
227 off_t
Seek(off_t offset
, int whence
) override
;
230 ReadStream
* const stream_
; // The UDIF stream.
231 const uint16_t block_size_
; // The UDIF block size.
232 const UDIFBlock
* const block_
; // The block for this partition.
233 uint64_t current_chunk_
; // The current chunk number.
234 // The current chunk stream.
235 scoped_ptr
<UDIFBlockChunkReadStream
> chunk_stream_
;
237 DISALLOW_COPY_AND_ASSIGN(UDIFPartitionReadStream
);
240 // A ReadStream for a single block chunk, which transparently handles
242 class UDIFBlockChunkReadStream
: public ReadStream
{
244 UDIFBlockChunkReadStream(ReadStream
* stream
,
246 const UDIFBlockChunk
* chunk
);
247 ~UDIFBlockChunkReadStream() override
;
249 bool Read(uint8_t* buffer
, size_t buffer_size
, size_t* bytes_read
) override
;
250 // Seek only supports SEEK_SET.
251 off_t
Seek(off_t offset
, int whence
) override
;
253 bool IsAtEnd() { return offset_
>= length_in_bytes_
; }
255 const UDIFBlockChunk
* chunk() const { return chunk_
; }
256 size_t length_in_bytes() const { return length_in_bytes_
; }
259 bool CopyOutZeros(uint8_t* buffer
, size_t buffer_size
, size_t* bytes_read
);
260 bool CopyOutUncompressed(
261 uint8_t* buffer
, size_t buffer_size
, size_t* bytes_read
);
262 bool CopyOutDecompressed(
263 uint8_t* buffer
, size_t buffer_size
, size_t* bytes_read
);
264 bool HandleADC(uint8_t* buffer
, size_t buffer_size
, size_t* bytes_read
);
265 bool HandleZLib(uint8_t* buffer
, size_t buffer_size
, size_t* bytes_read
);
266 bool HandleBZ2(uint8_t* buffer
, size_t buffer_size
, size_t* bytes_read
);
268 // Reads from |stream_| |chunk_->compressed_length| bytes, starting at
269 // |chunk_->compressed_offset| into |out_data|.
270 bool ReadCompressedData(std::vector
<uint8_t>* out_data
);
272 ReadStream
* const stream_
; // The UDIF stream.
273 const UDIFBlockChunk
* const chunk_
; // The chunk to be read.
274 size_t length_in_bytes_
; // The decompressed length in bytes.
275 size_t offset_
; // The offset into the decompressed buffer.
276 std::vector
<uint8_t> decompress_buffer_
; // Decompressed data buffer.
277 bool did_decompress_
; // Whether or not the chunk has been decompressed.
279 DISALLOW_COPY_AND_ASSIGN(UDIFBlockChunkReadStream
);
284 UDIFParser::UDIFParser(ReadStream
* stream
)
288 block_size_(kSectorSize
) {
291 UDIFParser::~UDIFParser() {}
293 bool UDIFParser::Parse() {
300 size_t UDIFParser::GetNumberOfPartitions() {
301 return blocks_
.size();
304 std::string
UDIFParser::GetPartitionName(size_t part_number
) {
305 DCHECK_LT(part_number
, partition_names_
.size());
306 return partition_names_
[part_number
];
309 std::string
UDIFParser::GetPartitionType(size_t part_number
) {
310 // The partition type is embedded in the Name field, as such:
311 // "Partition-Name (Partition-Type : Partition-ID)".
312 std::string name
= GetPartitionName(part_number
);
313 size_t open
= name
.rfind('(');
314 size_t separator
= name
.rfind(':');
315 if (open
== std::string::npos
|| separator
== std::string::npos
)
316 return std::string();
318 // Name does not end in ')' or no space after ':'.
319 if (*(name
.end() - 1) != ')' ||
320 (name
.size() - separator
< 2 || name
[separator
+ 1] != ' ')) {
321 return std::string();
326 if (separator
<= open
)
327 return std::string();
328 return name
.substr(open
, separator
- open
);
331 size_t UDIFParser::GetPartitionSize(size_t part_number
) {
332 DCHECK_LT(part_number
, blocks_
.size());
334 base::CheckedNumeric
<size_t>(blocks_
[part_number
]->sector_count()) *
336 return size
.ValueOrDie();
339 scoped_ptr
<ReadStream
> UDIFParser::GetPartitionReadStream(size_t part_number
) {
340 DCHECK_LT(part_number
, blocks_
.size());
341 return make_scoped_ptr(
342 new UDIFPartitionReadStream(stream_
, block_size_
, blocks_
[part_number
]));
345 bool UDIFParser::ParseBlkx() {
346 UDIFResourceFile trailer
;
347 if (stream_
->Seek(-sizeof(trailer
), SEEK_END
) == -1)
350 if (!stream_
->ReadType(&trailer
)) {
351 DLOG(ERROR
) << "Failed to read UDIFResourceFile";
354 ConvertBigEndian(&trailer
);
356 if (trailer
.signature
!= trailer
.kSignature
) {
357 DLOG(ERROR
) << "blkx signature does not match, is 0x"
358 << std::hex
<< trailer
.signature
;
361 if (trailer
.version
!= trailer
.kVersion
) {
362 DLOG(ERROR
) << "blkx version does not match, is " << trailer
.version
;
366 std::vector
<uint8_t> plist_bytes(trailer
.plist_length
, 0);
368 if (stream_
->Seek(trailer
.plist_offset
, SEEK_SET
) == -1)
371 if (!stream_
->ReadExact(&plist_bytes
[0], trailer
.plist_length
)) {
372 DLOG(ERROR
) << "Failed to read blkx plist data";
376 base::ScopedCFTypeRef
<CFDataRef
> plist_data(
377 CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
,
378 &plist_bytes
[0], plist_bytes
.size(), kCFAllocatorNull
));
380 DLOG(ERROR
) << "Failed to create data from bytes";
384 CFErrorRef error
= nullptr;
385 base::ScopedCFTypeRef
<CFDictionaryRef
> plist(
386 base::mac::CFCast
<CFDictionaryRef
>(
387 CFPropertyListCreateWithData(kCFAllocatorDefault
,
389 kCFPropertyListImmutable
,
392 base::scoped_policy::RETAIN
);
393 base::ScopedCFTypeRef
<CFErrorRef
> error_ref(error
);
395 base::ScopedCFTypeRef
<CFStringRef
> error_string(
396 CFErrorCopyDescription(error
));
397 DLOG(ERROR
) << "Failed to parse XML plist: "
398 << base::SysCFStringRefToUTF8(error_string
);
403 DLOG(ERROR
) << "Plist is not a dictionary";
407 auto resource_fork
= base::mac::GetValueFromDictionary
<CFDictionaryRef
>(
408 plist
.get(), CFSTR("resource-fork"));
409 if (!resource_fork
) {
410 DLOG(ERROR
) << "No resource-fork entry in plist";
414 auto blkx
= base::mac::GetValueFromDictionary
<CFArrayRef
>(resource_fork
,
417 DLOG(ERROR
) << "No blkx entry in resource-fork";
421 for (CFIndex i
= 0; i
< CFArrayGetCount(blkx
); ++i
) {
422 auto block_dictionary
=
423 base::mac::CFCast
<CFDictionaryRef
>(CFArrayGetValueAtIndex(blkx
, i
));
424 auto data
= base::mac::GetValueFromDictionary
<CFDataRef
>(block_dictionary
,
427 DLOG(ERROR
) << "Skipping block " << i
428 << " because it has no Data section";
432 // Copy the block table out of the plist.
434 reinterpret_cast<const UDIFBlockData
*>(CFDataGetBytePtr(data
));
435 scoped_ptr
<UDIFBlock
> block(new UDIFBlock(block_data
));
437 if (block
->signature() != UDIFBlockData::kSignature
) {
438 DLOG(ERROR
) << "Skipping block " << i
<< " because its signature does not"
439 << " match, is 0x" << std::hex
<< block
->signature();
442 if (block
->version() != UDIFBlockData::kVersion
) {
443 DLOG(ERROR
) << "Skipping block " << i
<< "because its version does not "
444 << "match, is " << block
->version();
448 CFStringRef partition_name_cf
= base::mac::CFCast
<CFStringRef
>(
449 CFDictionaryGetValue(block_dictionary
, CFSTR("Name")));
450 if (!partition_name_cf
) {
451 DLOG(ERROR
) << "Skipping block " << i
<< " because it has no name";
454 std::string partition_name
= base::SysCFStringRefToUTF8(partition_name_cf
);
456 if (DLOG_IS_ON(INFO
) && VLOG_IS_ON(1)) {
457 DVLOG(1) << "Name: " << partition_name
;
458 DVLOG(1) << "StartSector = " << block
->start_sector()
459 << ", SectorCount = " << block
->sector_count()
460 << ", ChunkCount = " << block
->chunk_count();
461 for (uint32_t j
= 0; j
< block
->chunk_count(); ++j
) {
462 const UDIFBlockChunk
* chunk
= block
->chunk(j
);
463 DVLOG(1) << "Chunk#" << j
464 << " type = " << std::hex
<< static_cast<uint32_t>(chunk
->type
)
465 << ", StartSector = " << std::dec
<< chunk
->start_sector
466 << ", SectorCount = " << chunk
->sector_count
467 << ", CompressOffset = " << chunk
->compressed_offset
468 << ", CompressLen = " << chunk
->compressed_length
;
472 blocks_
.push_back(block
.Pass());
473 partition_names_
.push_back(partition_name
);
479 bool UDIFParser::ReadBlockChunk(const UDIFBlockChunk
* chunk
,
480 std::vector
<uint8_t>* decompressed_data
) {
481 UDIFBlockChunkReadStream
chunk_read_stream(stream_
, block_size_
, chunk
);
482 decompressed_data
->resize(chunk_read_stream
.length_in_bytes());
483 return chunk_read_stream
.ReadExact(&(*decompressed_data
)[0],
484 decompressed_data
->size());
489 UDIFPartitionReadStream::UDIFPartitionReadStream(
492 const UDIFBlock
* partition_block
)
494 block_size_(block_size
),
495 block_(partition_block
),
500 UDIFPartitionReadStream::~UDIFPartitionReadStream() {}
502 bool UDIFPartitionReadStream::Read(uint8_t* buffer
,
504 size_t* bytes_read
) {
505 size_t buffer_space_remaining
= buffer_size
;
508 for (uint32_t i
= current_chunk_
; i
< block_
->chunk_count(); ++i
) {
509 const UDIFBlockChunk
* chunk
= block_
->chunk(i
);
511 // If this is the last block chunk, then the read is complete.
512 if (chunk
->type
== UDIFBlockChunk::Type::LAST_BLOCK
) {
516 // If the buffer is full, then the read is complete.
517 if (buffer_space_remaining
== 0)
520 // A chunk stream may exist if the last read from this chunk was partial,
521 // or if the stream was Seek()ed.
522 if (!chunk_stream_
) {
524 new UDIFBlockChunkReadStream(stream_
, block_size_
, chunk
));
526 DCHECK_EQ(chunk
, chunk_stream_
->chunk());
528 size_t chunk_bytes_read
= 0;
529 if (!chunk_stream_
->Read(&buffer
[buffer_size
- buffer_space_remaining
],
530 buffer_space_remaining
,
531 &chunk_bytes_read
)) {
532 DLOG(ERROR
) << "Failed to read " << buffer_space_remaining
<< " bytes "
533 << "from chunk " << i
;
536 *bytes_read
+= chunk_bytes_read
;
537 buffer_space_remaining
-= chunk_bytes_read
;
539 if (chunk_stream_
->IsAtEnd()) {
540 chunk_stream_
.reset();
548 off_t
UDIFPartitionReadStream::Seek(off_t offset
, int whence
) {
549 // Translate SEEK_END to SEEK_SET. SEEK_CUR is not currently supported.
550 if (whence
== SEEK_END
) {
551 base::CheckedNumeric
<off_t
> safe_offset(block_
->sector_count());
552 safe_offset
*= block_size_
;
553 safe_offset
+= offset
;
554 if (!safe_offset
.IsValid()) {
555 DLOG(ERROR
) << "Seek offset overflows";
558 offset
= safe_offset
.ValueOrDie();
559 } else if (whence
!= SEEK_SET
) {
560 DCHECK_EQ(SEEK_SET
, whence
);
563 uint64_t sector
= offset
/ block_size_
;
565 // Find the chunk for this sector.
566 uint32_t chunk_number
= 0;
567 const UDIFBlockChunk
* chunk
= nullptr;
568 for (uint32_t i
= 0; i
< block_
->chunk_count(); ++i
) {
569 const UDIFBlockChunk
* chunk_it
= block_
->chunk(i
);
570 // This assumes that all the chunks are ordered by sector.
573 chunk_it
->start_sector
< block_
->chunk(i
- 1)->start_sector
)
574 << "Chunks are not ordered by sector at chunk " << i
575 << " , previous start_sector = "
576 << block_
->chunk(i
- 1)->start_sector
<< ", current = "
577 << chunk_it
->start_sector
;
579 if (sector
>= chunk_it
->start_sector
) {
587 DLOG(ERROR
) << "Failed to Seek to partition offset " << offset
;
591 // Compute the offset into the chunk.
592 uint64_t offset_in_sector
= offset
% block_size_
;
593 uint64_t start_sector
= sector
- chunk
->start_sector
;
594 base::CheckedNumeric
<uint64_t> decompress_read_offset(start_sector
);
595 decompress_read_offset
*= block_size_
;
596 decompress_read_offset
+= offset_in_sector
;
598 if (!decompress_read_offset
.IsValid()) {
599 DLOG(ERROR
) << "Partition decompress read offset overflows";
603 if (!chunk_stream_
|| chunk
!= chunk_stream_
->chunk()) {
605 new UDIFBlockChunkReadStream(stream_
, block_size_
, chunk
));
607 current_chunk_
= chunk_number
;
608 chunk_stream_
->Seek(decompress_read_offset
.ValueOrDie(), SEEK_SET
);
613 UDIFBlockChunkReadStream::UDIFBlockChunkReadStream(ReadStream
* stream
,
615 const UDIFBlockChunk
* chunk
)
618 length_in_bytes_(chunk
->sector_count
* block_size
),
620 decompress_buffer_(),
621 did_decompress_(false) {
622 // Make sure the multiplication above did not overflow.
623 CHECK(length_in_bytes_
== 0 || length_in_bytes_
>= block_size
);
626 UDIFBlockChunkReadStream::~UDIFBlockChunkReadStream() {
629 bool UDIFBlockChunkReadStream::Read(uint8_t* buffer
,
631 size_t* bytes_read
) {
632 switch (chunk_
->type
) {
633 case UDIFBlockChunk::Type::ZERO_FILL
:
634 case UDIFBlockChunk::Type::IGNORED
:
635 return CopyOutZeros(buffer
, buffer_size
, bytes_read
);
636 case UDIFBlockChunk::Type::UNCOMPRESSED
:
637 return CopyOutUncompressed(buffer
, buffer_size
, bytes_read
);
638 case UDIFBlockChunk::Type::COMPRESS_ADC
:
639 return HandleADC(buffer
, buffer_size
, bytes_read
);
640 case UDIFBlockChunk::Type::COMPRESS_ZLIB
:
641 return HandleZLib(buffer
, buffer_size
, bytes_read
);
642 case UDIFBlockChunk::Type::COMPRESSS_BZ2
:
643 return HandleBZ2(buffer
, buffer_size
, bytes_read
);
644 case UDIFBlockChunk::Type::COMMENT
:
647 case UDIFBlockChunk::Type::LAST_BLOCK
:
654 off_t
UDIFBlockChunkReadStream::Seek(off_t offset
, int whence
) {
655 DCHECK_EQ(SEEK_SET
, whence
);
656 DCHECK_LT(static_cast<uint64_t>(offset
), length_in_bytes_
);
661 bool UDIFBlockChunkReadStream::CopyOutZeros(uint8_t* buffer
,
663 size_t* bytes_read
) {
664 *bytes_read
= std::min(buffer_size
, length_in_bytes_
- offset_
);
665 bzero(buffer
, *bytes_read
);
666 offset_
+= *bytes_read
;
670 bool UDIFBlockChunkReadStream::CopyOutUncompressed(uint8_t* buffer
,
672 size_t* bytes_read
) {
673 *bytes_read
= std::min(buffer_size
, length_in_bytes_
- offset_
);
675 if (*bytes_read
== 0)
678 uint64_t offset
= chunk_
->compressed_offset
+ offset_
;
679 if (stream_
->Seek(offset
, SEEK_SET
) == -1)
682 bool rv
= stream_
->Read(buffer
, *bytes_read
, bytes_read
);
684 offset_
+= *bytes_read
;
686 DLOG(ERROR
) << "Failed to read uncompressed chunk data";
690 bool UDIFBlockChunkReadStream::CopyOutDecompressed(uint8_t* buffer
,
692 size_t* bytes_read
) {
693 DCHECK(did_decompress_
);
694 *bytes_read
= std::min(buffer_size
, decompress_buffer_
.size() - offset_
);
695 memcpy(buffer
, &decompress_buffer_
[offset_
], *bytes_read
);
696 offset_
+= *bytes_read
;
700 bool UDIFBlockChunkReadStream::HandleADC(uint8_t* buffer
,
702 size_t* bytes_read
) {
703 // TODO(rsesek): Implement ADC handling.
708 bool UDIFBlockChunkReadStream::HandleZLib(uint8_t* buffer
,
710 size_t* bytes_read
) {
711 if (!did_decompress_
) {
712 std::vector
<uint8_t> compressed_data(chunk_
->compressed_length
, 0);
713 if (!ReadCompressedData(&compressed_data
))
717 if (inflateInit(&zlib
) != Z_OK
) {
718 DLOG(ERROR
) << "Failed to initialize zlib";
722 decompress_buffer_
.resize(length_in_bytes_
);
723 zlib
.next_in
= &compressed_data
[0];
724 zlib
.avail_in
= compressed_data
.size();
725 zlib
.next_out
= &decompress_buffer_
[0];
726 zlib
.avail_out
= decompress_buffer_
.size();
728 int rv
= inflate(&zlib
, Z_FINISH
);
731 if (rv
!= Z_STREAM_END
) {
732 DLOG(ERROR
) << "Failed to decompress zlib data, error = " << rv
;
736 did_decompress_
= true;
739 return CopyOutDecompressed(buffer
, buffer_size
, bytes_read
);
742 bool UDIFBlockChunkReadStream::HandleBZ2(uint8_t* buffer
,
744 size_t* bytes_read
) {
745 if (!did_decompress_
) {
746 std::vector
<uint8_t> compressed_data(chunk_
->compressed_length
, 0);
747 if (!ReadCompressedData(&compressed_data
))
751 if (BZ2_bzDecompressInit(&bz
, 0, 0) != BZ_OK
) {
752 DLOG(ERROR
) << "Failed to initialize bzlib";
756 decompress_buffer_
.resize(length_in_bytes_
);
757 bz
.next_in
= reinterpret_cast<char*>(&compressed_data
[0]);
758 bz
.avail_in
= compressed_data
.size();
759 bz
.next_out
= reinterpret_cast<char*>(&decompress_buffer_
[0]);
760 bz
.avail_out
= decompress_buffer_
.size();
762 int rv
= BZ2_bzDecompress(&bz
);
763 BZ2_bzDecompressEnd(&bz
);
765 if (rv
!= BZ_STREAM_END
) {
766 DLOG(ERROR
) << "Failed to decompress BZ2 data, error = " << rv
;
770 did_decompress_
= true;
773 return CopyOutDecompressed(buffer
, buffer_size
, bytes_read
);
776 bool UDIFBlockChunkReadStream::ReadCompressedData(
777 std::vector
<uint8_t>* out_data
) {
778 DCHECK_EQ(chunk_
->compressed_length
, out_data
->size());
780 if (stream_
->Seek(chunk_
->compressed_offset
, SEEK_SET
) == -1)
783 if (!stream_
->ReadExact(&(*out_data
)[0], out_data
->size())) {
784 DLOG(ERROR
) << "Failed to read chunk compressed data at "
785 << chunk_
->compressed_offset
;
794 } // namespace safe_browsing