2020-12-05 13:08:24 +01:00
|
|
|
#include "stdafx.h"
|
2017-02-16 03:15:00 +01:00
|
|
|
|
|
|
|
|
#include "TAR.h"
|
|
|
|
|
|
2021-03-11 00:26:39 +01:00
|
|
|
#include "util/asm.hpp"
|
|
|
|
|
|
2017-02-16 03:15:00 +01:00
|
|
|
#include <cmath>
|
|
|
|
|
#include <cstdlib>
|
2021-03-11 00:26:39 +01:00
|
|
|
#include <charconv>
|
2017-02-16 03:15:00 +01:00
|
|
|
|
2020-02-01 08:43:43 +01:00
|
|
|
LOG_CHANNEL(tar_log, "TAR");
|
2020-02-01 05:15:50 +01:00
|
|
|
|
2021-03-11 00:26:39 +01:00
|
|
|
tar_object::tar_object(const fs::file& file)
|
2017-02-22 14:08:53 +01:00
|
|
|
: m_file(file)
|
2017-02-16 03:15:00 +01:00
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-11 00:26:39 +01:00
|
|
|
TARHeader tar_object::read_header(u64 offset) const
|
2017-02-16 03:15:00 +01:00
|
|
|
{
|
2021-03-11 00:26:39 +01:00
|
|
|
TARHeader header{};
|
|
|
|
|
|
|
|
|
|
if (m_file.seek(offset) != offset)
|
|
|
|
|
{
|
|
|
|
|
return header;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!m_file.read(header))
|
|
|
|
|
{
|
|
|
|
|
std::memset(&header, 0, sizeof(header));
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-16 03:15:00 +01:00
|
|
|
return header;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-11 00:26:39 +01:00
|
|
|
u64 octal_text_to_u64(std::string_view sv)
|
2017-02-16 03:15:00 +01:00
|
|
|
{
|
2021-03-11 00:26:39 +01:00
|
|
|
u64 i = -1;
|
|
|
|
|
const auto ptr = std::from_chars(sv.data(), sv.data() + sv.size(), i, 8).ptr;
|
|
|
|
|
|
|
|
|
|
// Range must be terminated with either NUL or space
|
|
|
|
|
if (ptr == sv.data() + sv.size() || (*ptr && *ptr != ' '))
|
2017-02-16 03:15:00 +01:00
|
|
|
{
|
2021-03-11 00:26:39 +01:00
|
|
|
i = -1;
|
2017-02-16 03:15:00 +01:00
|
|
|
}
|
2021-03-11 00:26:39 +01:00
|
|
|
|
|
|
|
|
return i;
|
2017-02-16 03:15:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::vector<std::string> tar_object::get_filenames()
|
|
|
|
|
{
|
|
|
|
|
std::vector<std::string> vec;
|
|
|
|
|
get_file("");
|
|
|
|
|
for (auto it = m_map.cbegin(); it != m_map.cend(); ++it)
|
|
|
|
|
{
|
|
|
|
|
vec.push_back(it->first);
|
|
|
|
|
}
|
|
|
|
|
return vec;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-11 00:26:39 +01:00
|
|
|
fs::file tar_object::get_file(const std::string& path)
|
2017-02-16 03:15:00 +01:00
|
|
|
{
|
2017-02-22 14:08:53 +01:00
|
|
|
if (!m_file) return fs::file();
|
2017-02-16 03:15:00 +01:00
|
|
|
|
2021-03-11 00:26:39 +01:00
|
|
|
if (auto it = m_map.find(path); it != m_map.end())
|
2017-02-16 03:15:00 +01:00
|
|
|
{
|
2021-03-11 00:26:39 +01:00
|
|
|
u64 size = 0;
|
|
|
|
|
std::memcpy(&size, it->second.second.size, sizeof(size));
|
2017-02-16 03:15:00 +01:00
|
|
|
std::vector<u8> buf(size);
|
2021-03-11 00:26:39 +01:00
|
|
|
m_file.seek(it->second.first);
|
2017-02-16 03:15:00 +01:00
|
|
|
m_file.read(buf, size);
|
|
|
|
|
return fs::make_stream(std::move(buf));
|
|
|
|
|
}
|
|
|
|
|
else //continue scanning from last file entered
|
|
|
|
|
{
|
2021-03-11 00:26:39 +01:00
|
|
|
const u64 max_size = m_file.size();
|
|
|
|
|
|
|
|
|
|
while (largest_offset < max_size)
|
2017-02-22 14:08:53 +01:00
|
|
|
{
|
2017-02-16 03:15:00 +01:00
|
|
|
TARHeader header = read_header(largest_offset);
|
|
|
|
|
|
2021-03-11 00:26:39 +01:00
|
|
|
u64 size = -1;
|
2017-02-16 03:15:00 +01:00
|
|
|
|
2021-03-11 00:26:39 +01:00
|
|
|
if (header.name[0] && std::memcmp(header.magic, "ustar", 5) == 0)
|
|
|
|
|
{
|
|
|
|
|
const std::string_view size_sv{header.size, std::size(header.size)};
|
|
|
|
|
|
|
|
|
|
size = octal_text_to_u64(size_sv);
|
|
|
|
|
|
|
|
|
|
// Check for overflows and if surpasses file size
|
|
|
|
|
if (size + 512 > size && max_size >= size + 512 && max_size - size - 512 >= largest_offset)
|
|
|
|
|
{
|
|
|
|
|
// Cache size in native u64 format
|
|
|
|
|
static_assert(sizeof(size) < sizeof(header.size));
|
|
|
|
|
std::memcpy(header.size, &size, 8);
|
|
|
|
|
|
|
|
|
|
// Save header andd offset
|
|
|
|
|
m_map.insert_or_assign(header.name, std::make_pair(largest_offset + 512, header));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Invalid
|
|
|
|
|
size = -1;
|
|
|
|
|
tar_log.error("tar_object::get_file() failed to convert header.size=%s, filesize=0x%x", size_sv, max_size);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
tar_log.trace("tar_object::get_file() failed to parse header: offset=0x%x, filesize=0x%x", largest_offset, max_size);
|
|
|
|
|
}
|
2017-02-16 03:15:00 +01:00
|
|
|
|
2021-03-11 00:26:39 +01:00
|
|
|
if (size == umax)
|
|
|
|
|
{
|
|
|
|
|
size = 0;
|
|
|
|
|
header.name[0] = '\0'; // Ensure path will not be equal
|
2017-02-16 03:15:00 +01:00
|
|
|
}
|
2021-03-11 00:26:39 +01:00
|
|
|
|
|
|
|
|
if (!path.empty() && path == header.name)
|
|
|
|
|
{
|
|
|
|
|
// Path is equal, read file and advance offset to start of next block
|
|
|
|
|
std::vector<u8> buf(size);
|
|
|
|
|
|
|
|
|
|
if (m_file.read(buf, size))
|
|
|
|
|
{
|
|
|
|
|
largest_offset += utils::align(size, 512) + 512;
|
|
|
|
|
return fs::make_stream(std::move(buf));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tar_log.error("tar_object::get_file() failed to read file entry %s (size=0x%x)", header.name, size);
|
|
|
|
|
size = 0;
|
2017-02-16 03:15:00 +01:00
|
|
|
}
|
2021-03-11 00:26:39 +01:00
|
|
|
|
|
|
|
|
// Advance offset to next block
|
|
|
|
|
largest_offset += utils::align(size, 512) + 512;
|
2017-02-22 14:08:53 +01:00
|
|
|
}
|
2018-06-23 08:26:11 +02:00
|
|
|
|
2017-02-16 03:15:00 +01:00
|
|
|
return fs::file();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-23 08:26:11 +02:00
|
|
|
bool tar_object::extract(std::string path, std::string ignore)
|
2017-02-16 03:15:00 +01:00
|
|
|
{
|
2017-02-22 14:08:53 +01:00
|
|
|
if (!m_file) return false;
|
2017-02-16 03:15:00 +01:00
|
|
|
|
2021-03-11 00:26:39 +01:00
|
|
|
get_file(""); // Make sure we have scanned all files
|
|
|
|
|
|
|
|
|
|
for (auto& iter : m_map)
|
2017-02-16 03:15:00 +01:00
|
|
|
{
|
2021-03-11 00:26:39 +01:00
|
|
|
const TARHeader& header = iter.second.second;
|
|
|
|
|
const std::string& name = iter.first;
|
2018-06-23 08:26:11 +02:00
|
|
|
|
2021-03-11 00:26:39 +01:00
|
|
|
std::string result = path + name;
|
2018-06-23 08:26:11 +02:00
|
|
|
|
|
|
|
|
if (result.compare(path.size(), ignore.size(), ignore) == 0)
|
|
|
|
|
{
|
|
|
|
|
result.erase(path.size(), ignore.size());
|
|
|
|
|
}
|
2017-02-16 03:15:00 +01:00
|
|
|
|
2017-02-22 14:08:53 +01:00
|
|
|
switch (header.filetype)
|
|
|
|
|
{
|
2021-03-11 00:26:39 +01:00
|
|
|
case '\0':
|
2017-02-16 03:15:00 +01:00
|
|
|
case '0':
|
|
|
|
|
{
|
2021-03-11 00:26:39 +01:00
|
|
|
auto data = get_file(name).release();
|
2020-04-11 23:47:58 +02:00
|
|
|
|
2018-06-23 08:26:11 +02:00
|
|
|
fs::file file(result, fs::rewrite);
|
2020-04-11 23:47:58 +02:00
|
|
|
|
|
|
|
|
if (file)
|
|
|
|
|
{
|
|
|
|
|
file.write(static_cast<fs::container_stream<std::vector<u8>>*>(data.get())->obj);
|
2021-03-11 00:26:39 +01:00
|
|
|
tar_log.notice("TAR Loader: written file %s", name);
|
2020-04-11 23:47:58 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-11 00:26:39 +01:00
|
|
|
const auto old_error = fs::g_tls_error;
|
|
|
|
|
tar_log.error("TAR Loader: failed to write file %s (%s) (fs::exists=%s)", name, old_error, fs::exists(result));
|
2020-04-11 23:47:58 +02:00
|
|
|
return false;
|
2017-02-16 03:15:00 +01:00
|
|
|
}
|
2018-06-23 08:26:11 +02:00
|
|
|
|
2017-02-16 03:15:00 +01:00
|
|
|
case '5':
|
|
|
|
|
{
|
2021-02-28 20:59:27 +01:00
|
|
|
if (!fs::create_path(result))
|
|
|
|
|
{
|
2021-03-11 00:26:39 +01:00
|
|
|
tar_log.error("TAR Loader: failed to create directory %s (%s)", name, fs::g_tls_error);
|
2021-02-28 20:59:27 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-16 03:15:00 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
default:
|
2020-02-01 05:15:50 +01:00
|
|
|
tar_log.error("TAR Loader: unknown file type: 0x%x", header.filetype);
|
2017-02-16 03:15:00 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
2017-02-22 14:08:53 +01:00
|
|
|
}
|