1 // 2 // Copyright (C) 2016 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_generator/xz.h" 18 19 #include <7zCrc.h> 20 #include <Xz.h> 21 #include <XzEnc.h> 22 23 #include <algorithm> 24 25 #include <base/logging.h> 26 27 namespace { 28 29 bool xz_initialized = false; 30 31 // An ISeqInStream implementation that reads all the data from the passed Blob. 32 struct BlobReaderStream : public ISeqInStream { 33 explicit BlobReaderStream(const brillo::Blob& data) : data_(data) { 34 Read = &BlobReaderStream::ReadStatic; 35 } 36 37 static SRes ReadStatic(void* p, void* buf, size_t* size) { 38 auto* self = 39 static_cast<BlobReaderStream*>(reinterpret_cast<ISeqInStream*>(p)); 40 *size = std::min(*size, self->data_.size() - self->pos_); 41 memcpy(buf, self->data_.data() + self->pos_, *size); 42 self->pos_ += *size; 43 return SZ_OK; 44 } 45 46 const brillo::Blob& data_; 47 48 // The current reader position. 49 size_t pos_ = 0; 50 }; 51 52 // An ISeqOutStream implementation that writes all the data to the passed Blob. 53 struct BlobWriterStream : public ISeqOutStream { 54 explicit BlobWriterStream(brillo::Blob* data) : data_(data) { 55 Write = &BlobWriterStream::WriteStatic; 56 } 57 58 static size_t WriteStatic(void* p, const void* buf, size_t size) { 59 auto* self = 60 static_cast<BlobWriterStream*>(reinterpret_cast<ISeqOutStream*>(p)); 61 const uint8_t* buffer = reinterpret_cast<const uint8_t*>(buf); 62 self->data_->reserve(self->data_->size() + size); 63 self->data_->insert(self->data_->end(), buffer, buffer + size); 64 return size; 65 } 66 67 brillo::Blob* data_; 68 }; 69 70 } // namespace 71 72 namespace chromeos_update_engine { 73 74 void XzCompressInit() { 75 if (xz_initialized) 76 return; 77 xz_initialized = true; 78 // Although we don't include a CRC32 for the stream, the xz file header has 79 // a CRC32 of the header itself, which required the CRC table to be 80 // initialized. 81 CrcGenerateTable(); 82 } 83 84 bool XzCompress(const brillo::Blob& in, brillo::Blob* out) { 85 CHECK(xz_initialized) << "Initialize XzCompress first"; 86 out->clear(); 87 if (in.empty()) 88 return true; 89 90 // Xz compression properties. 91 CXzProps props; 92 XzProps_Init(&props); 93 // No checksum in the xz stream. xz-embedded (used by the decompressor) only 94 // supports CRC32, but we already check the sha-1 of the whole blob during 95 // payload application. 96 props.checkId = XZ_CHECK_NO; 97 98 // LZMA2 compression properties. 99 CLzma2EncProps lzma2Props; 100 props.lzma2Props = &lzma2Props; 101 Lzma2EncProps_Init(&lzma2Props); 102 // LZMA compression "level 6" requires 9 MB of RAM to decompress in the worst 103 // case. 104 lzma2Props.lzmaProps.level = 6; 105 lzma2Props.lzmaProps.numThreads = 1; 106 // The input size data is used to reduce the dictionary size if possible. 107 lzma2Props.lzmaProps.reduceSize = in.size(); 108 Lzma2EncProps_Normalize(&lzma2Props); 109 110 BlobWriterStream out_writer(out); 111 BlobReaderStream in_reader(in); 112 SRes res = Xz_Encode(&out_writer, &in_reader, &props, nullptr /* progress */); 113 return res == SZ_OK; 114 } 115 116 } // namespace chromeos_update_engine 117