text: // 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>
  #include <base/logging.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
  