blob: 79ca493541220c80f63a7edbc0de0917457c1764 [file] [log] [blame]
// 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_reader.h"
#include <optional>
#include "base/check.h"
#include "base/numerics/safe_conversions.h"
namespace {
// https://www.gnu.org/software/tar/manual/html_node/Standard.html
constexpr size_t kHeaderSize = 512;
constexpr size_t kFileSizeFieldOffset = 124;
constexpr size_t kFileSizeFieldLength = 12;
} // namespace
SingleFileTarReader::SingleFileTarReader() = default;
SingleFileTarReader::~SingleFileTarReader() = default;
bool SingleFileTarReader::ExtractChunk(base::span<const uint8_t> src_buffer,
base::span<const uint8_t>& dst_buffer) {
dst_buffer = src_buffer;
// We haven't read the header.
if (!tar_content_size_.has_value()) {
if (src_buffer.size() < kHeaderSize)
return false;
// TODO(tetsui): check the file header checksum
tar_content_size_ = ReadOctalNumber(
src_buffer.subspan(kFileSizeFieldOffset, kFileSizeFieldLength));
if (!tar_content_size_.has_value())
return false;
// Drop the header.
dst_buffer = dst_buffer.subspan(kHeaderSize);
}
if (bytes_processed_ > tar_content_size_.value())
return false;
const uint64_t bytes_remaining = tar_content_size_.value() - bytes_processed_;
// A tar file always has a padding at the end of the file. If `dst_buffer`
// contains the padding, drop it.
if (dst_buffer.size() > bytes_remaining) {
// The comparison above guarantees that `checked_cast` will succeed:
dst_buffer = dst_buffer.first(base::checked_cast<size_t>(bytes_remaining));
}
bytes_processed_ += dst_buffer.size();
return true;
}
bool SingleFileTarReader::IsComplete() const {
if (!tar_content_size_.has_value())
return false;
return tar_content_size_.value() <= bytes_processed_;
}
// static
std::optional<uint64_t> SingleFileTarReader::ReadOctalNumber(
base::span<const uint8_t> buffer) {
const size_t length = buffer.size();
if (length < 8u)
return std::nullopt;
uint64_t num = 0;
// In GNU tar extension, when the number starts with an invalid ASCII
// character 0x80, then non-leading 8 bytes of the field should be interpreted
// as a big-endian integer.
// https://www.gnu.org/software/tar/manual/html_node/Extensions.html
if (buffer[0] == 0x80) {
for (size_t i = length - 8; i < length; ++i) {
num <<= 8;
num += buffer[i];
}
return num;
}
for (size_t i = 0; i < length; ++i) {
const char as_char = static_cast<char>(buffer[i]);
if (as_char == '\0')
break;
num *= 8;
num += as_char - '0';
}
return num;
}