1 // 2 // Copyright (C) 2015 The Android Open Source Project 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 17 #include "update_engine/payload_consumer/xz_extent_writer.h" 18 19 using std::vector; 20 21 namespace chromeos_update_engine { 22 23 namespace { 24 const brillo::Blob::size_type kOutputBufferLength = 16 * 1024; 25 26 // xz uses a variable dictionary size which impacts on the compression ratio 27 // and is required to be reconstructed in RAM during decompression. While we 28 // control the required memory from the compressor side, the decompressor allows 29 // to set a limit on this dictionary size, rejecting compressed streams that 30 // require more than that. "xz -9" requires up to 64 MiB, so a 64 MiB limit 31 // will allow compressed streams up to -9, the maximum compression setting. 32 const uint32_t kXzMaxDictSize = 64 * 1024 * 1024; 33 34 const char* XzErrorString(enum xz_ret error) { 35 #define __XZ_ERROR_STRING_CASE(code) case code: return #code; 36 switch (error) { 37 __XZ_ERROR_STRING_CASE(XZ_OK) 38 __XZ_ERROR_STRING_CASE(XZ_STREAM_END) 39 __XZ_ERROR_STRING_CASE(XZ_UNSUPPORTED_CHECK) 40 __XZ_ERROR_STRING_CASE(XZ_MEM_ERROR) 41 __XZ_ERROR_STRING_CASE(XZ_MEMLIMIT_ERROR) 42 __XZ_ERROR_STRING_CASE(XZ_FORMAT_ERROR) 43 __XZ_ERROR_STRING_CASE(XZ_OPTIONS_ERROR) 44 __XZ_ERROR_STRING_CASE(XZ_DATA_ERROR) 45 __XZ_ERROR_STRING_CASE(XZ_BUF_ERROR) 46 default: 47 return "<unknown xz error>"; 48 } 49 #undef __XZ_ERROR_STRING_CASE 50 }; 51 } // namespace 52 53 XzExtentWriter::~XzExtentWriter() { 54 xz_dec_end(stream_); 55 } 56 57 bool XzExtentWriter::Init(FileDescriptorPtr fd, 58 const vector<Extent>& extents, 59 uint32_t block_size) { 60 stream_ = xz_dec_init(XZ_DYNALLOC, kXzMaxDictSize); 61 TEST_AND_RETURN_FALSE(stream_ != nullptr); 62 return underlying_writer_->Init(fd, extents, block_size); 63 } 64 65 bool XzExtentWriter::Write(const void* bytes, size_t count) { 66 // Copy the input data into |input_buffer_| only if |input_buffer_| already 67 // contains unconsumed data. Otherwise, process the data directly from the 68 // source. 69 const uint8_t* input = reinterpret_cast<const uint8_t*>(bytes); 70 if (!input_buffer_.empty()) { 71 input_buffer_.insert(input_buffer_.end(), input, input + count); 72 input = input_buffer_.data(); 73 count = input_buffer_.size(); 74 } 75 76 xz_buf request; 77 request.in = input; 78 request.in_pos = 0; 79 request.in_size = count; 80 81 brillo::Blob output_buffer(kOutputBufferLength); 82 request.out = output_buffer.data(); 83 request.out_size = output_buffer.size(); 84 for (;;) { 85 request.out_pos = 0; 86 87 xz_ret ret = xz_dec_run(stream_, &request); 88 if (ret != XZ_OK && ret != XZ_STREAM_END) { 89 LOG(ERROR) << "xz_dec_run returned " << XzErrorString(ret); 90 return false; 91 } 92 93 if (request.out_pos == 0) 94 break; 95 96 TEST_AND_RETURN_FALSE( 97 underlying_writer_->Write(output_buffer.data(), request.out_pos)); 98 if (ret == XZ_STREAM_END) 99 CHECK_EQ(request.in_size, request.in_pos); 100 if (request.in_size == request.in_pos) 101 break; // No more input to process. 102 } 103 output_buffer.clear(); 104 105 // Store unconsumed data (if any) in |input_buffer_|. Since |input| can point 106 // to the existing |input_buffer_| we create a new one before assigning it. 107 brillo::Blob new_input_buffer(request.in + request.in_pos, 108 request.in + request.in_size); 109 input_buffer_ = std::move(new_input_buffer); 110 return true; 111 } 112 113 bool XzExtentWriter::EndImpl() { 114 TEST_AND_RETURN_FALSE(input_buffer_.empty()); 115 return underlying_writer_->End(); 116 } 117 118 } // namespace chromeos_update_engine 119