#include "icsneo/disk/diskwritedriver.h" #include using namespace icsneo; using namespace icsneo::Disk; std::optional WriteDriver::writeLogicalDisk(Communication& com, device_eventhandler_t report, ReadDriver& readDriver, uint64_t pos, const uint8_t* from, uint64_t amount, std::chrono::milliseconds timeout, MemoryType memType) { if(amount == 0) return 0; std::optional ret; const uint32_t idealBlockSize = getBlockSizeBounds().second; // Write from here if we need to read-modify-write a block // That would be the case either if we don't want some at the // beginning or end of the block. std::vector alignedWriteBuffer(idealBlockSize); pos += vsaOffset; const uint64_t startBlock = pos / idealBlockSize; const uint32_t posWithinFirstBlock = static_cast(pos % idealBlockSize); uint64_t blocks = amount / idealBlockSize + (amount % idealBlockSize ? 1 : 0); if(blocks * idealBlockSize - posWithinFirstBlock < amount) blocks++; // We need one more block to get the last partial block's worth uint64_t blocksProcessed = 0; while(blocksProcessed < blocks && timeout >= std::chrono::milliseconds::zero()) { const uint64_t currentBlock = startBlock + blocksProcessed; uint64_t fromOffset = blocksProcessed * idealBlockSize; if(fromOffset < posWithinFirstBlock) fromOffset = 0; else fromOffset -= posWithinFirstBlock; const uint32_t posWithinCurrentBlock = (blocksProcessed ? 0 : posWithinFirstBlock); uint32_t curAmt = idealBlockSize - posWithinCurrentBlock; const auto amountLeft = amount - ret.value_or(0); if(curAmt > amountLeft) curAmt = static_cast(amountLeft); auto start = std::chrono::high_resolution_clock::now(); const auto reportFromRead = [&report, &blocksProcessed](APIEvent::Type t, APIEvent::Severity s) { if(t == APIEvent::Type::ParameterOutOfRange && blocksProcessed) t = APIEvent::Type::EOFReached; report(t, s); }; const bool useAlignedWriteBuffer = (posWithinCurrentBlock != 0 || curAmt != idealBlockSize); if(useAlignedWriteBuffer) { auto read = readDriver.readLogicalDisk(com, reportFromRead, currentBlock * idealBlockSize, alignedWriteBuffer.data(), idealBlockSize, timeout, memType); timeout -= std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start); if(read != idealBlockSize) break; // readLogicalDisk reports its own errors memcpy(alignedWriteBuffer.data() + posWithinCurrentBlock, from + fromOffset, curAmt); } start = std::chrono::high_resolution_clock::now(); auto bytesTransferred = writeLogicalDiskAligned(com, report, currentBlock * idealBlockSize, useAlignedWriteBuffer ? alignedWriteBuffer.data() : (from + fromOffset), idealBlockSize, timeout, memType); timeout -= std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start); if(!bytesTransferred.has_value() || *bytesTransferred < curAmt) { if(timeout < std::chrono::milliseconds::zero()) report(APIEvent::Type::Timeout, APIEvent::Severity::Error); else report((blocksProcessed || bytesTransferred.value_or(0u) != 0u) ? APIEvent::Type::EOFReached : APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error); break; } if(!ret) ret.emplace(); *ret += std::min(*bytesTransferred, curAmt); blocksProcessed++; } // No matter how much succeeded, to be safe, we'll invalidate anything // we may have even tried to write, since it may have succeeded without // notifying, etc. readDriver.invalidateCache(pos, amount, memType); return ret; }