blob: 5c92d05a15b7200921b5febee32763acbfa5699f [file] [log] [blame]
Alex Deymo2e71f902015-09-30 01:25:48 -07001//
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
Kelvin Zhange47767a2023-05-16 13:00:58 -070017#include "update_engine/common/utils.h"
Alex Deymo39910dc2015-11-09 17:04:30 -080018#include "update_engine/payload_consumer/xz_extent_writer.h"
Alex Deymo2e71f902015-09-30 01:25:48 -070019
Amin Hassanicd7edbe2017-09-18 17:05:02 -070020using google::protobuf::RepeatedPtrField;
Alex Deymo2e71f902015-09-30 01:25:48 -070021
22namespace chromeos_update_engine {
23
24namespace {
Alex Vakulenko3f39d5c2015-10-13 09:27:13 -070025const brillo::Blob::size_type kOutputBufferLength = 16 * 1024;
Alex Deymo2e71f902015-09-30 01:25:48 -070026
27// xz uses a variable dictionary size which impacts on the compression ratio
28// and is required to be reconstructed in RAM during decompression. While we
29// control the required memory from the compressor side, the decompressor allows
30// to set a limit on this dictionary size, rejecting compressed streams that
31// require more than that. "xz -9" requires up to 64 MiB, so a 64 MiB limit
32// will allow compressed streams up to -9, the maximum compression setting.
33const uint32_t kXzMaxDictSize = 64 * 1024 * 1024;
34
35const char* XzErrorString(enum xz_ret error) {
Amin Hassani008c4582019-01-13 16:22:47 -080036#define __XZ_ERROR_STRING_CASE(code) \
37 case code: \
38 return #code;
Alex Deymo2e71f902015-09-30 01:25:48 -070039 switch (error) {
40 __XZ_ERROR_STRING_CASE(XZ_OK)
41 __XZ_ERROR_STRING_CASE(XZ_STREAM_END)
42 __XZ_ERROR_STRING_CASE(XZ_UNSUPPORTED_CHECK)
43 __XZ_ERROR_STRING_CASE(XZ_MEM_ERROR)
44 __XZ_ERROR_STRING_CASE(XZ_MEMLIMIT_ERROR)
45 __XZ_ERROR_STRING_CASE(XZ_FORMAT_ERROR)
46 __XZ_ERROR_STRING_CASE(XZ_OPTIONS_ERROR)
47 __XZ_ERROR_STRING_CASE(XZ_DATA_ERROR)
48 __XZ_ERROR_STRING_CASE(XZ_BUF_ERROR)
49 default:
50 return "<unknown xz error>";
51 }
Amin Hassani008c4582019-01-13 16:22:47 -080052#undef __XZ_ERROR_STRING_CASE
Amin Hassanicd7edbe2017-09-18 17:05:02 -070053}
Alex Deymo2e71f902015-09-30 01:25:48 -070054} // namespace
55
56XzExtentWriter::~XzExtentWriter() {
Kelvin Zhangcc04de72021-10-05 14:43:02 -070057 stream_.reset();
Sen Jiang5e1af982018-11-01 15:01:45 -070058 TEST_AND_RETURN(input_buffer_.empty());
Alex Deymo2e71f902015-09-30 01:25:48 -070059}
60
Kelvin Zhang4d22ca22021-02-09 14:06:25 -050061bool XzExtentWriter::Init(const RepeatedPtrField<Extent>& extents,
Alex Deymo2e71f902015-09-30 01:25:48 -070062 uint32_t block_size) {
Kelvin Zhangcc04de72021-10-05 14:43:02 -070063 stream_.reset(xz_dec_init(XZ_DYNALLOC, kXzMaxDictSize));
Alex Deymo2e71f902015-09-30 01:25:48 -070064 TEST_AND_RETURN_FALSE(stream_ != nullptr);
Kelvin Zhang4d22ca22021-02-09 14:06:25 -050065 return underlying_writer_->Init(extents, block_size);
Alex Deymo2e71f902015-09-30 01:25:48 -070066}
67
68bool XzExtentWriter::Write(const void* bytes, size_t count) {
69 // Copy the input data into |input_buffer_| only if |input_buffer_| already
70 // contains unconsumed data. Otherwise, process the data directly from the
71 // source.
72 const uint8_t* input = reinterpret_cast<const uint8_t*>(bytes);
73 if (!input_buffer_.empty()) {
74 input_buffer_.insert(input_buffer_.end(), input, input + count);
75 input = input_buffer_.data();
76 count = input_buffer_.size();
77 }
78
Kelvin Zhange47767a2023-05-16 13:00:58 -070079 xz_buf request{};
Alex Deymo2e71f902015-09-30 01:25:48 -070080 request.in = input;
81 request.in_pos = 0;
82 request.in_size = count;
83
Alex Vakulenko3f39d5c2015-10-13 09:27:13 -070084 brillo::Blob output_buffer(kOutputBufferLength);
Alex Deymo2e71f902015-09-30 01:25:48 -070085 request.out = output_buffer.data();
86 request.out_size = output_buffer.size();
87 for (;;) {
88 request.out_pos = 0;
89
Kelvin Zhangcc04de72021-10-05 14:43:02 -070090 xz_ret ret = xz_dec_run(stream_.get(), &request);
Alex Deymo2e71f902015-09-30 01:25:48 -070091 if (ret != XZ_OK && ret != XZ_STREAM_END) {
92 LOG(ERROR) << "xz_dec_run returned " << XzErrorString(ret);
93 return false;
94 }
95
96 if (request.out_pos == 0)
97 break;
98
99 TEST_AND_RETURN_FALSE(
100 underlying_writer_->Write(output_buffer.data(), request.out_pos));
101 if (ret == XZ_STREAM_END)
102 CHECK_EQ(request.in_size, request.in_pos);
103 if (request.in_size == request.in_pos)
104 break; // No more input to process.
105 }
106 output_buffer.clear();
107
108 // Store unconsumed data (if any) in |input_buffer_|. Since |input| can point
109 // to the existing |input_buffer_| we create a new one before assigning it.
Alex Vakulenko3f39d5c2015-10-13 09:27:13 -0700110 brillo::Blob new_input_buffer(request.in + request.in_pos,
111 request.in + request.in_size);
Alex Deymo2e71f902015-09-30 01:25:48 -0700112 input_buffer_ = std::move(new_input_buffer);
113 return true;
114}
115
Alex Deymo2e71f902015-09-30 01:25:48 -0700116} // namespace chromeos_update_engine