// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved. // For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md #include #include #include #include FileContext::FileContext(const std::filesystem::path &path, std::size_t size, std::size_t chunkSize, std::size_t offset) : path(path), size(size), offset(offset), chunkSize(chunkSize) { if (!size || !chunkSize) { throw std::invalid_argument("Invalid FileContext arguments"); } runningCrc32Digest.reset(); } FileContext::~FileContext() { } FileReadContext::FileReadContext(const std::filesystem::path &path, std::size_t size, std::size_t chunkSize, std::size_t offset) : FileContext(path, size, chunkSize, offset) {} FileReadContext::~FileReadContext() {} FileWriteContext::FileWriteContext(const std::filesystem::path &path, std::size_t size, std::size_t chunkSize, std::string crc32Digest, std::size_t offset) : FileContext(path, size, chunkSize, offset), crc32Digest(std::move(crc32Digest)) {} FileWriteContext::~FileWriteContext() {} auto FileContext::advanceFileOffset(std::size_t bySize) -> void { offset += bySize; } auto FileContext::reachedEOF() const -> bool { return offset >= size; } auto FileContext::chunksInQuantity(std::size_t quantity) const -> std::size_t { return (quantity + chunkSize - 1) / chunkSize; } auto FileContext::totalChunksInFile() const -> std::size_t { return chunksInQuantity(size); } auto FileContext::expectedChunkInFile() const -> std::size_t { return 1 + chunksInQuantity(offset); } auto FileContext::validateChunkRequest(std::uint32_t chunkNo) const -> bool { return !(chunkNo < 1 || chunkNo > totalChunksInFile() || chunkNo != expectedChunkInFile()); } auto FileContext::fileHash() const -> std::string { return runningCrc32Digest.getHash(); } auto FileReadContext::read() -> std::vector { LOG_DEBUG("Getting file data"); std::ifstream file(path, std::ios::binary); if (!file.is_open() || file.fail()) { LOG_ERROR("File %s open error", path.c_str()); throw std::runtime_error("File open error"); } file.seekg(offset); auto dataLeft = std::min(static_cast(chunkSize), (size - offset)); std::vector buffer(dataLeft); file.read(reinterpret_cast(buffer.data()), dataLeft); if (file.bad()) { LOG_ERROR("File %s read error", path.c_str()); throw std::runtime_error("File read error"); } runningCrc32Digest.add(buffer.data(), dataLeft); LOG_DEBUG("Read %u bytes", static_cast(dataLeft)); advanceFileOffset(dataLeft); if (reachedEOF()) { LOG_INFO("Reached EOF"); } return buffer; } auto FileWriteContext::write(const std::vector &data) -> void { LOG_DEBUG("Sending file data"); std::ofstream file(path, std::ios::binary | std::ios::app); if (!file.is_open() || file.fail()) { LOG_ERROR("File %s open error", path.c_str()); throw std::runtime_error("File open error"); } file.seekp(offset); auto dataLeft = std::min(static_cast(chunkSize), (size - offset)); file.write(reinterpret_cast(data.data()), dataLeft); file.flush(); if (file.bad()) { LOG_ERROR("File %s write error", path.c_str()); throw std::runtime_error("File write error"); } runningCrc32Digest.add(data.data(), dataLeft); LOG_DEBUG("Written %u bytes", static_cast(dataLeft)); advanceFileOffset(dataLeft); if (reachedEOF()) { LOG_INFO("Reached EOF of %s", path.c_str()); } } auto FileWriteContext::crc32Matches() const -> bool { LOG_DEBUG("Hash: %s", fileHash().c_str()); return crc32Digest == fileHash(); } auto FileWriteContext::removeFile() -> void { std::error_code ec; std::filesystem::remove(path, ec); }