blob: 2821eda0b1f14375a057783261ed516aa4b547b3 [file]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/services/file_util/single_file_tar_file_extractor.h"
#include <utility>
#include <vector>
#include "base/containers/span.h"
#include "chrome/services/file_util/public/mojom/constants.mojom.h"
#include "chrome/services/file_util/single_file_tar_reader.h"
namespace {
constexpr int kTarBufferSize = 8192;
// TarExtractorInner extracts a .TAR file and writes the extracted data to the
// destination file.
class TarExtractorInner {
public:
TarExtractorInner(
mojo::PendingRemote<chrome::mojom::SingleFileExtractorListener>
pending_listener,
base::File src_file,
base::File dst_file)
: listener_(std::move(pending_listener)),
src_file_(std::move(src_file)),
dst_file_(std::move(dst_file)) {}
chrome::file_util::mojom::ExtractionResult Extract() {
std::vector<uint8_t> tar_buffer;
while (true) {
tar_buffer = ReadTarFile();
if (tar_buffer.empty()) {
return chrome::file_util::mojom::ExtractionResult::kGenericError;
}
base::span<const uint8_t> output_file_content;
if (!tar_reader_.ExtractChunk(base::make_span(tar_buffer),
output_file_content)) {
return chrome::file_util::mojom::ExtractionResult::kInvalidSrcFile;
}
// TODO(sahok): Utilize this logic and Deduplicate the same logic in
// single_file_tar_xz_file_extractor.cc
if (!dst_file_.WriteAtCurrentPosAndCheck(output_file_content))
return chrome::file_util::mojom::ExtractionResult::kDstFileError;
if (tar_reader_.IsComplete())
return chrome::file_util::mojom::ExtractionResult::kSuccess;
listener_->OnProgress(tar_reader_.tar_content_size().value(),
tar_reader_.bytes_processed());
}
}
void CloseFiles() {
src_file_.Close();
dst_file_.Close();
}
private:
// ReadTarFile reads .tar file and returns the contents. ReadTarFile reads
// kTarBufferSize bytes at most, and might return an empty vector on error.
// Returned vector is resized to actual bytes read.
std::vector<uint8_t> ReadTarFile() {
std::vector<uint8_t> tar_buffer(kTarBufferSize);
const int bytes_read = src_file_.ReadAtCurrentPos(
reinterpret_cast<char*>(tar_buffer.data()), kTarBufferSize);
if (bytes_read < 0)
return std::vector<uint8_t>();
if (bytes_read == 0) {
// After reading the last chunk of file content, it is expected that
// tar_reader_.IsComplete() in the Extract function returns true and the
// .tar.xz file extraction ends.
return std::vector<uint8_t>();
}
tar_buffer.resize(bytes_read);
return tar_buffer;
}
const mojo::Remote<chrome::mojom::SingleFileExtractorListener> listener_;
base::File src_file_;
base::File dst_file_;
SingleFileTarReader tar_reader_;
};
} // namespace
SingleFileTarFileExtractor::SingleFileTarFileExtractor() = default;
SingleFileTarFileExtractor::~SingleFileTarFileExtractor() = default;
void SingleFileTarFileExtractor::Extract(
base::File src_file,
base::File dst_file,
mojo::PendingRemote<chrome::mojom::SingleFileExtractorListener>
pending_listener,
SingleFileExtractor::ExtractCallback callback) {
if (!src_file.IsValid() || !dst_file.IsValid()) {
std::move(callback).Run(
chrome::file_util::mojom::ExtractionResult::kGenericError);
return;
}
TarExtractorInner extractor(std::move(pending_listener), std::move(src_file),
std::move(dst_file));
chrome::file_util::mojom::ExtractionResult result = extractor.Extract();
// Explicitly close the files before calling `callback` to ensure all
// extracted data is flushed to the file and the file is closed.
extractor.CloseFiles();
std::move(callback).Run(result);
}