Home | History | Annotate | Download | only in payload_consumer
      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 google::protobuf::RepeatedPtrField;
     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 RepeatedPtrField<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