Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
195 changes: 195 additions & 0 deletions Back to the future/BackToTheFuture/BackToTheFuture/FileManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -266,3 +266,198 @@ std::string FileManager::sha256File(const fs::path& path)
LOG(Info, "Exit.");
return ss.str();
}

void FileManager::Unpack(const fs::path& archivePath, const fs::path& destRoot)
{
LOG(Info, "Entry.");

std::ifstream ifstream(archivePath, std::ios::binary);
if (!ifstream)
{
LOG(Error, "Cannot open archive.");
return;
}

char magic[4];
ifstream.read(magic, 4);
if (memcmp(magic, MAGIC, 4) != 0)
{
LOG(Error, "Invalid archive magic.");
return;
}

uint32_t version = read_u32(ifstream);
uint32_t numBlobs = read_u32(ifstream);
uint32_t numFiles = read_u32(ifstream);

struct Blob
{
uint64_t origSize;
uint64_t compSize;
std::streampos pos;
};

std::unordered_map<std::string, Blob> blobMap(numBlobs);

//read blobs
for (uint32_t i = 0; i < numBlobs; i++)
{
std::ostringstream shaHex;
binToHexSHA(ifstream, shaHex);

uint64_t origSize = read_u64(ifstream);
uint64_t compSize = read_u64(ifstream);
std::streampos pos = ifstream.tellg();

ifstream.seekg(static_cast<std::streamoff>(compSize), std::ios::cur);
if (!ifstream)
{
LOG(Error, "Corrupted archive while skipping blob data.");
return;
}

blobMap.emplace(shaHex.str(), Blob{ origSize, compSize, pos });
}

// Read metaddata and create files
for (uint32_t i = 0; i < numFiles; i++)
{
uint32_t pathLen = read_u32(ifstream);
std::string relpath(pathLen, '\0');
if (!ifstream.read(&relpath[0], pathLen))
{
LOG(Error, "Corrupted archive while reading path.");
return;
}

std::ostringstream shaHex;
binToHexSHA(ifstream, shaHex);

uint64_t size = read_u64(ifstream);
uint32_t readonly = read_u32(ifstream);
uint64_t mtime = read_u64(ifstream);

auto it = blobMap.find(shaHex.str());
if (it == blobMap.end())
{
LOG(Error, "Missing blob for file: %s", relpath.c_str());
return;
}

const Blob& blob = it->second;

// save metadata pos
std::streampos metadata_pos = ifstream.tellg();

// go to blobs
ifstream.clear();
ifstream.seekg(blob.pos);
if (!ifstream)
{
LOG(Error, "Seekg failed for blob: %s", relpath.c_str());
return;
}

fs::path outPath = destRoot / relpath;
fs::create_directories(outPath.parent_path());

decompresStreamToFile(ifstream, blob.compSize, outPath);

// back to metadata
ifstream.clear();
ifstream.seekg(metadata_pos);

if (readonly)
{
auto perms = fs::status(outPath).permissions();
fs::permissions(outPath, perms & ~fs::perms::owner_write);
}

auto ftime = fs::file_time_type::clock::time_point(std::chrono::seconds(mtime));
fs::last_write_time(outPath, ftime);
}

LOG(Info, "Exit.");
}

void FileManager::decompresStreamToFile(std::istream& iStream, uint64_t compresedSize, const fs::path& outPath)
{
std::vector<char> in(CHUNK), out(CHUNK);
z_stream zStream{};

if (inflateInit(&zStream) != Z_OK)
{
LOG(Error, "InflateInit failed.");
return;
}

std::ofstream ofStream(outPath, std::ios::binary);

if (!ofStream)
{
LOG(Error, "Cannot create output file %s.", outPath.string().c_str());
return;
}

uint64_t remaining = compresedSize;

while (0 < remaining)
{
int toRead = static_cast<int>(std::min<uint64_t>(CHUNK, remaining));
iStream.read(in.data(), toRead);
int got = iStream.gcount();

if (0 >= got)
{
LOG(Error, "Unexpected EOF.");
return;
}

remaining -= got;
zStream.next_in = reinterpret_cast<Bytef*>(in.data());
zStream.avail_in = got;

do
{
zStream.next_out = reinterpret_cast<Bytef*>(out.data());
zStream.avail_out = CHUNK;

int ret = inflate(&zStream, Z_NO_FLUSH);

if (0 > ret)
{
LOG(Error, "inflate error.");
}

uInt have = CHUNK - zStream.avail_out;
if (0 < have)
{
ofStream.write(out.data(), have);
}
} while (0 == zStream.avail_out);
}

inflateEnd(&zStream);
}

void FileManager::binToHexSHA(std::ifstream& ifstream, std::ostringstream& shaHex)
{
LOG(Info, "Entry.");

unsigned char shaBin[32];
if (!ifstream.read(reinterpret_cast<char*>(shaBin), 32))
{
LOG(Error, "Corrupted archive while reading file SHA.");
return;
}

shaHex << std::hex << std::setfill('0');
for (int j = 0; j < 32; j++)
{
shaHex << std::setw(2) << static_cast<int>(shaBin[j]);
}

LOG(Info, "Hex SHA: %s", shaHex.str().c_str());

LOG(Info, "Exit.");
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
#include <vector>
#include <filesystem>

#include "Logger.hpp"

namespace fs = std::filesystem;

struct FileMetadata
Expand All @@ -20,19 +22,27 @@ class IFileManager
{
public:
virtual void Pack(const fs::path& root, const fs::path& archivePath) = 0;
virtual void Unpack(const fs::path& archivepath, const fs::path& destRoot) = 0;
};

class FileManager : IFileManager
{
public:

void Pack(const fs::path& root, const fs::path& archivePath) override;
void Unpack(const fs::path& archivepath, const fs::path& destRoot) override;

private:

inline void write_u32(std::ostream& os, uint32_t v) { for (int i = 0; i < 4; i++) os.put((char)((v >> (8 * i)) & 0xFF)); }
inline void write_u64(std::ostream& os, uint64_t v) { for (int i = 0; i < 8; i++) os.put((char)((v >> (8 * i)) & 0xFF)); }
inline uint32_t read_u32(std::istream& is) { uint32_t v = 0; for (int i = 0; i < 4; i++) { int c = is.get(); if (c == EOF) LOG(Error, "Unexpected EOF"); v |= (uint32_t)c << (8 * i); } return v; }
inline uint64_t read_u64(std::istream& is) { uint64_t v = 0; for (int i = 0; i < 8; i++) { int c = is.get(); if (c == EOF) LOG(Error, "Unexpected EOF"); v |= (uint64_t)c << (8 * i); } return v; }

std::vector<FileMetadata> scanFiles(const fs::path& root);
uint64_t compressFileToStream(const fs::path& path, std::ostream& ostream);
void decompresStreamToFile(std::istream& istream, uint64_t compresedSize, const fs::path& outPath);
std::string sha256File(const fs::path& path);
};

void binToHexSHA(std::ifstream& ifstream, std::ostringstream& shaHex);
};