1 // Copyright 2017 The Chromium OS 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.
4 
5 #include "bsdiff/bz2_decompressor.h"
6 
7 #include <algorithm>
8 #include <limits>
9 
10 #include <bzlib.h>
11 
12 #include "bsdiff/logging.h"
13 
14 namespace bsdiff {
15 
16 BZ2Decompressor::~BZ2Decompressor() {
17   // Release the memory on destruction if needed.
18   if (stream_initialized_)
19     BZ2_bzDecompressEnd(&stream_);
20 }
21 
22 bool BZ2Decompressor::SetInputData(const uint8_t* input_data, size_t size) {
23   // TODO(xunchang) update the avail_in for size > 2GB.
24   if (size > std::numeric_limits<unsigned int>::max()) {
25     LOG(ERROR) << "Oversized input data" << size;
26     return false;
27   }
28 
29   stream_.next_in = reinterpret_cast<char*>(const_cast<uint8_t*>(input_data));
30   stream_.avail_in = size;
31   stream_.bzalloc = nullptr;
32   stream_.bzfree = nullptr;
33   stream_.opaque = nullptr;
34   int bz2err = BZ2_bzDecompressInit(&stream_, 0, 0);
35   if (bz2err != BZ_OK) {
36     LOG(ERROR) << "Failed to bzinit control stream: " << bz2err;
37     return false;
38   }
39   stream_initialized_ = true;
40   return true;
41 }
42 
43 bool BZ2Decompressor::Read(uint8_t* output_data, size_t bytes_to_output) {
44   if (!stream_initialized_) {
45     LOG(ERROR) << "BZ2Decompressor not initialized";
46     return false;
47   }
48   stream_.next_out = reinterpret_cast<char*>(output_data);
49   while (bytes_to_output > 0) {
50     size_t output_size = std::min<size_t>(
51         std::numeric_limits<unsigned int>::max(), bytes_to_output);
52     stream_.avail_out = output_size;
53 
54     size_t prev_avail_in = stream_.avail_in;
55     int bz2err = BZ2_bzDecompress(&stream_);
56     if (bz2err != BZ_OK && bz2err != BZ_STREAM_END) {
57       LOG(ERROR) << "Failed to decompress " << output_size
58                  << " bytes of data, error: " << bz2err;
59       return false;
60     }
61     bytes_to_output -= (output_size - stream_.avail_out);
62     if (bytes_to_output && prev_avail_in == stream_.avail_in &&
63         output_size == stream_.avail_out) {
64       LOG(ERROR) << "BZ2Decompressor made no progress, pending "
65                  << bytes_to_output << " bytes to decompress";
66       return false;
67     }
68   }
69   return true;
70 }
71 
72 bool BZ2Decompressor::Close() {
73   if (!stream_initialized_) {
74     LOG(ERROR) << "BZ2Decompressor not initialized";
75     return false;
76   }
77   int bz2err = BZ2_bzDecompressEnd(&stream_);
78   if (bz2err != BZ_OK) {
79     LOG(ERROR) << "BZ2_bzDecompressEnd returns with " << bz2err;
80     return false;
81   }
82   stream_initialized_ = false;
83   return true;
84 }
85 
86 }  // namespace bsdiff
87