blob: 63b54efee2a3c30208ad5fbab93c1b88a483c25d [file] [log] [blame]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "archive_reader.h"
#include <filesystem>
#include <iostream>
#include "tools/android/devil_util/archive_helper.h"
ArchiveReader::ArchiveReader() = default;
ArchiveReader::~ArchiveReader() = default;
void ArchiveReader::ReadMagicBytes(char** input_buffer,
size_t* input_buffer_size) {
if (*input_buffer_size == 0) {
return;
}
if (magic_bytes_pos_ == kMagicBytesLength) {
return;
}
uint64_t size_to_read = std::min(static_cast<uint64_t>(*input_buffer_size),
kMagicBytesLength - magic_bytes_pos_);
std::memcpy(magic_bytes_buffer_ + magic_bytes_pos_, *input_buffer,
size_to_read);
*input_buffer += size_to_read;
*input_buffer_size -= size_to_read;
magic_bytes_pos_ += size_to_read;
if (magic_bytes_pos_ == kMagicBytesLength) {
size_t magic_bytes_actual_length =
strnlen(magic_bytes_buffer_, kMagicBytesLength);
magic_bytes_ = std::string(magic_bytes_buffer_, magic_bytes_actual_length);
}
}
void ArchiveReader::ReadCurMemberPathLength(char** input_buffer,
size_t* input_buffer_size) {
if (*input_buffer_size == 0) {
return;
}
if (cur_member_path_length_pos_ == kPathLengthSize) {
return;
}
uint64_t size_to_read =
std::min(static_cast<uint64_t>(*input_buffer_size),
kPathLengthSize - cur_member_path_length_pos_);
std::memcpy(cur_member_path_length_buffer_ + cur_member_path_length_pos_,
*input_buffer, size_to_read);
*input_buffer += size_to_read;
*input_buffer_size -= size_to_read;
cur_member_path_length_pos_ += size_to_read;
if (cur_member_path_length_pos_ == kPathLengthSize) {
size_t path_length_actual_length =
strnlen(cur_member_path_length_buffer_, kPathLengthSize);
std::string path_length_str(cur_member_path_length_buffer_,
path_length_actual_length);
cur_member_path_length_ = stoull(path_length_str);
}
}
void ArchiveReader::ReadCurMemberPath(char** input_buffer,
size_t* input_buffer_size) {
if (*input_buffer_size == 0) {
return;
}
if (cur_member_path_pos_ == cur_member_path_length_) {
return;
}
uint64_t size_to_read =
std::min(static_cast<uint64_t>(*input_buffer_size),
cur_member_path_length_ - cur_member_path_pos_);
std::memcpy(cur_member_path_buffer_ + cur_member_path_pos_, *input_buffer,
size_to_read);
*input_buffer += size_to_read;
*input_buffer_size -= size_to_read;
cur_member_path_pos_ += size_to_read;
if (cur_member_path_pos_ == cur_member_path_length_) {
cur_member_path_ =
std::string(cur_member_path_buffer_, cur_member_path_length_);
std::filesystem::path path(cur_member_path_);
// Create the parent directories if necessary.
std::filesystem::create_directories(path.parent_path());
// Open the output stream which will also create the file.
cur_member_ofstream_ =
std::ofstream(cur_member_path_, std::ios::binary | std::ios::trunc);
if (cur_member_ofstream_.fail()) {
std::cerr << "Failed to open the file at: " << cur_member_path_
<< std::endl;
exit(1);
}
// Set the file permission to std::filesystem::perms::all which is 0777.
std::filesystem::permissions(cur_member_path_, std::filesystem::perms::all);
}
}
void ArchiveReader::ReadCurMemberContentLength(char** input_buffer,
size_t* input_buffer_size) {
if (*input_buffer_size == 0) {
return;
}
if (cur_member_content_length_pos_ == kContentLengthSize) {
return;
}
uint64_t size_to_read =
std::min(static_cast<uint64_t>(*input_buffer_size),
kContentLengthSize - cur_member_content_length_pos_);
std::memcpy(
cur_member_content_length_buffer_ + cur_member_content_length_pos_,
*input_buffer, size_to_read);
*input_buffer += size_to_read;
*input_buffer_size -= size_to_read;
cur_member_content_length_pos_ += size_to_read;
if (cur_member_content_length_pos_ == kContentLengthSize) {
size_t content_length_actual_length =
strnlen(cur_member_content_length_buffer_, kContentLengthSize);
std::string content_length_str(cur_member_content_length_buffer_,
content_length_actual_length);
cur_member_content_length_ = stoull(content_length_str);
}
}
void ArchiveReader::ReadCurMemberContent(char** input_buffer,
size_t* input_buffer_size) {
if (*input_buffer_size == 0) {
return;
}
if (cur_member_content_pos_ == cur_member_content_length_) {
return;
}
uint64_t size_to_read =
std::min(static_cast<uint64_t>(*input_buffer_size),
cur_member_content_length_ - cur_member_content_pos_);
cur_member_ofstream_.write(*input_buffer, size_to_read);
if (cur_member_ofstream_.fail()) {
std::cerr << "Failed to write to the file at: " << cur_member_path_
<< std::endl;
exit(1);
}
*input_buffer += size_to_read;
*input_buffer_size -= size_to_read;
cur_member_content_pos_ += size_to_read;
}
bool ArchiveReader::ExtractArchiveStreaming(char* input_buffer,
size_t input_buffer_size) {
if (input_buffer_size == 0) {
return false;
}
ReadMagicBytes(&input_buffer, &input_buffer_size);
if (magic_bytes_pos_ < kMagicBytesLength) {
return false;
} else {
if (magic_bytes_ != kDevilUtilArchiveV1MagicBytes) {
std::cerr << "The magic bytes at the beginning of the archive does not "
"match the expected value!"
<< std::endl;
std::cerr << "Expected magic bytes: " << kDevilUtilArchiveV1MagicBytes
<< std::endl;
std::cerr << "Actual magic bytes: " << magic_bytes_ << std::endl;
exit(1);
}
}
while (true) {
// If we are processing a new member, reset all internal states.
if (start_new_member_) {
cur_member_path_length_pos_ = 0;
cur_member_path_pos_ = 0;
cur_member_content_length_pos_ = 0;
cur_member_content_pos_ = 0;
start_new_member_ = false;
}
ReadCurMemberPathLength(&input_buffer, &input_buffer_size);
// A path length of 0 indicates the end of the archive.
if (cur_member_path_length_pos_ == kPathLengthSize &&
cur_member_path_length_ == 0) {
return true;
}
ReadCurMemberPath(&input_buffer, &input_buffer_size);
ReadCurMemberContentLength(&input_buffer, &input_buffer_size);
ReadCurMemberContent(&input_buffer, &input_buffer_size);
if (cur_member_content_length_pos_ == kContentLengthSize &&
cur_member_content_pos_ == cur_member_content_length_ &&
cur_member_path_length_pos_ == kPathLengthSize &&
cur_member_path_pos_ == cur_member_path_length_) {
// We have finished reading the current member.
cur_member_ofstream_.close();
start_new_member_ = true;
} else {
// We have only partially read the current member.
start_new_member_ = false;
return false;
}
}
}