| // Copyright 2018 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "modemfwd/file_decompressor.h" |
| |
| #include <lzma.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| |
| #include <memory> |
| |
| #include <base/files/file.h> |
| |
| namespace modemfwd { |
| |
| bool DecompressXzFile(const base::FilePath& in_file_path, |
| const base::FilePath& out_file_path) { |
| base::File in_file(in_file_path, |
| base::File::FLAG_OPEN | base::File::FLAG_READ); |
| if (!in_file.IsValid()) { |
| LOG(ERROR) << "Failed to open '" << in_file_path.value() << "' for read"; |
| return false; |
| } |
| |
| base::File out_file(out_file_path, |
| base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE); |
| if (!out_file.IsValid()) { |
| LOG(ERROR) << "Failed to open '" << out_file_path.value() << "' for write"; |
| return false; |
| } |
| |
| lzma_stream stream = LZMA_STREAM_INIT; |
| lzma_ret ret = lzma_stream_decoder(&stream, UINT64_MAX, 0); |
| if (ret != LZMA_OK) { |
| LOG(ERROR) << "Failed initialize LZMA decoder, error=" << ret; |
| return false; |
| } |
| |
| std::unique_ptr<lzma_stream, decltype(&lzma_end)> auto_stream_deleter( |
| &stream, &lzma_end); |
| |
| lzma_action action = LZMA_RUN; |
| const size_t in_buffer_size = BUFSIZ; |
| const size_t out_buffer_size = BUFSIZ; |
| auto in_buffer = std::make_unique<uint8_t[]>(in_buffer_size); |
| auto out_buffer = std::make_unique<uint8_t[]>(out_buffer_size); |
| |
| stream.next_in = nullptr; |
| stream.avail_in = 0; |
| stream.next_out = out_buffer.get(); |
| stream.avail_out = out_buffer_size; |
| |
| for (;;) { |
| if (stream.avail_in == 0) { |
| int read_ret = in_file.ReadAtCurrentPos( |
| reinterpret_cast<char*>(in_buffer.get()), in_buffer_size); |
| if (read_ret < 0) { |
| PLOG(ERROR) << "Failed to read from '" << out_file_path.value() << "'"; |
| return false; |
| } |
| |
| if (read_ret == 0) |
| action = LZMA_FINISH; |
| |
| stream.next_in = in_buffer.get(); |
| stream.avail_in = read_ret; |
| } |
| |
| ret = lzma_code(&stream, action); |
| |
| // Flushes the decoded data from the output buffer to the output file. |
| if (stream.avail_out == 0 || ret == LZMA_STREAM_END) { |
| size_t write_size = out_buffer_size - stream.avail_out; |
| if (out_file.WriteAtCurrentPos(reinterpret_cast<char*>(out_buffer.get()), |
| write_size) != |
| static_cast<int>(write_size)) { |
| PLOG(ERROR) << "Failed to write to '" << out_file_path.value() << "'"; |
| return false; |
| } |
| |
| stream.next_out = out_buffer.get(); |
| stream.avail_out = out_buffer_size; |
| } |
| |
| // A LZMA_STREAM_END return value indicates that the stream has been |
| // decoded successfully. |
| if (ret == LZMA_STREAM_END) |
| break; |
| |
| // Otherwise, a return value other than LZMA_OK indicates an error. |
| if (ret != LZMA_OK) { |
| LOG(ERROR) << "Failed to decompress '" << in_file_path.value() |
| << "', error=" << ret; |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| } // namespace modemfwd |