diff --git a/device/device.cpp b/device/device.cpp index 96a4e9b..1513c36 100644 --- a/device/device.cpp +++ b/device/device.cpp @@ -488,6 +488,19 @@ optional Device::readLogicalDisk(uint64_t pos, uint8_t* into, uint64_t std::lock_guard lk(diskLock); + if(diskReadDriver->getAccess() == Disk::Access::EntireCard && diskWriteDriver->getAccess() == Disk::Access::VSA) { + // We have mismatched drivers, we need to add an offset to the diskReadDriver + const auto offset = Disk::FindVSAInFAT([this, &timeout](uint64_t pos, uint8_t *into, uint64_t amount) { + const auto start = std::chrono::steady_clock::now(); + auto ret = diskReadDriver->readLogicalDisk(*com, report, pos, into, amount, timeout); + timeout -= std::chrono::duration_cast(std::chrono::steady_clock::now() - start); + return ret; + }); + if(!offset.has_value()) + return nullopt; + diskReadDriver->setVSAOffset(*offset); + } + // This is needed for certain read drivers which take over the communication stream const auto lifetime = suppressDisconnects(); @@ -550,9 +563,18 @@ optional Device::getVSAOffsetInLogicalDisk() { if (diskReadDriver->getAccess() == Disk::Access::VSA || diskReadDriver->getAccess() == Disk::Access::None) return 0ull; - return Disk::FindVSAInFAT([this](uint64_t pos, uint8_t *into, uint64_t amount) { + auto offset = Disk::FindVSAInFAT([this](uint64_t pos, uint8_t *into, uint64_t amount) { return diskReadDriver->readLogicalDisk(*com, report, pos, into, amount); }); + if(!offset.has_value()) + return nullopt; + + if(diskReadDriver->getAccess() == Disk::Access::EntireCard && diskWriteDriver->getAccess() == Disk::Access::VSA) { + // We have mismatched drivers, we need to add an offset to the diskReadDriver + diskReadDriver->setVSAOffset(*offset); + return 0ull; + } + return *offset; } optional Device::getDigitalIO(IO type, size_t number /* = 1 */) { diff --git a/disk/diskreaddriver.cpp b/disk/diskreaddriver.cpp index d37e34d..29da933 100644 --- a/disk/diskreaddriver.cpp +++ b/disk/diskreaddriver.cpp @@ -16,6 +16,7 @@ optional ReadDriver::readLogicalDisk(Communication& com, device_eventh // beginning or end of the block. std::vector alignedReadBuffer; + pos += vsaOffset; const uint32_t idealBlockSize = getBlockSizeBounds().second; const uint64_t startBlock = pos / idealBlockSize; const uint32_t posWithinFirstBlock = static_cast(pos % idealBlockSize); diff --git a/disk/diskwritedriver.cpp b/disk/diskwritedriver.cpp index fe4ab46..3a0980b 100644 --- a/disk/diskwritedriver.cpp +++ b/disk/diskwritedriver.cpp @@ -25,6 +25,7 @@ optional WriteDriver::writeLogicalDisk(Communication& com, device_even // ensure an operation is atomic std::vector atomicBuffer(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); diff --git a/include/icsneo/disk/diskdriver.h b/include/icsneo/disk/diskdriver.h index 309e6e8..4fdf45b 100644 --- a/include/icsneo/disk/diskdriver.h +++ b/include/icsneo/disk/diskdriver.h @@ -25,8 +25,27 @@ enum class Access { class Driver { public: virtual ~Driver() = default; - virtual Access getAccess() const = 0; + Access getAccess() const { + if(vsaOffset) + return Access::VSA; + return getPossibleAccess(); + } virtual std::pair getBlockSizeBounds() const = 0; + + void setVSAOffset(uint64_t offset) { vsaOffset = offset; } + +protected: + uint64_t vsaOffset = 0; + +private: + /** + * Report the possible access that this driver has + * + * In some cases, such as a mismatched possible access between + * read and write drivers, this will be overridden in the driver + * layer. + */ + virtual Access getPossibleAccess() const = 0; }; } // namespace Disk diff --git a/include/icsneo/disk/extextractordiskreaddriver.h b/include/icsneo/disk/extextractordiskreaddriver.h index 539afb2..9d8f28a 100644 --- a/include/icsneo/disk/extextractordiskreaddriver.h +++ b/include/icsneo/disk/extextractordiskreaddriver.h @@ -16,7 +16,6 @@ namespace Disk { */ class ExtExtractorDiskReadDriver : public ReadDriver { public: - Access getAccess() const override { return Access::EntireCard; } std::pair getBlockSizeBounds() const override { static_assert(SectorSize <= std::numeric_limits::max(), "Incorrect sector size"); static_assert(SectorSize >= std::numeric_limits::min(), "Incorrect sector size"); @@ -36,6 +35,8 @@ private: uint8_t headerLength = 7; // Correct for Ethernet + Access getPossibleAccess() const override { return Access::EntireCard; } + optional readLogicalDiskAligned(Communication& com, device_eventhandler_t report, uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout) override; }; diff --git a/include/icsneo/disk/neomemorydiskreaddriver.h b/include/icsneo/disk/neomemorydiskreaddriver.h index aa28838..6c3cc37 100644 --- a/include/icsneo/disk/neomemorydiskreaddriver.h +++ b/include/icsneo/disk/neomemorydiskreaddriver.h @@ -18,7 +18,6 @@ namespace Disk { */ class NeoMemoryDiskReadDriver : public ReadDriver { public: - Access getAccess() const override { return Access::VSA; } std::pair getBlockSizeBounds() const override { static_assert(SectorSize <= std::numeric_limits::max(), "Incorrect sector size"); static_assert(SectorSize >= std::numeric_limits::min(), "Incorrect sector size"); @@ -33,6 +32,8 @@ private: uint64_t cachePos = 0; std::chrono::time_point cachedAt; + Access getPossibleAccess() const override { return Access::VSA; } + optional readLogicalDiskAligned(Communication& com, device_eventhandler_t report, uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout) override; }; diff --git a/include/icsneo/disk/nulldiskdriver.h b/include/icsneo/disk/nulldiskdriver.h index dd97c9a..e45815d 100644 --- a/include/icsneo/disk/nulldiskdriver.h +++ b/include/icsneo/disk/nulldiskdriver.h @@ -22,7 +22,6 @@ public: uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout = DefaultTimeout) override; optional writeLogicalDisk(Communication& com, device_eventhandler_t report, ReadDriver& readDriver, uint64_t pos, const uint8_t* from, uint64_t amount, std::chrono::milliseconds timeout = DefaultTimeout) override; - Access getAccess() const override { return Access::None; } std::pair getBlockSizeBounds() const override { static_assert(SectorSize <= std::numeric_limits::max(), "Incorrect sector size"); static_assert(SectorSize >= std::numeric_limits::min(), "Incorrect sector size"); @@ -30,6 +29,8 @@ public: } private: + Access getPossibleAccess() const override { return Access::None; } + optional readLogicalDiskAligned(Communication& com, device_eventhandler_t report, uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout) override; optional writeLogicalDiskAligned(Communication& com, device_eventhandler_t report, diff --git a/include/icsneo/disk/plasiondiskreaddriver.h b/include/icsneo/disk/plasiondiskreaddriver.h index 6a575e9..ee94265 100644 --- a/include/icsneo/disk/plasiondiskreaddriver.h +++ b/include/icsneo/disk/plasiondiskreaddriver.h @@ -16,7 +16,6 @@ namespace Disk { */ class PlasionDiskReadDriver : public ReadDriver { public: - Access getAccess() const override { return Access::EntireCard; } std::pair getBlockSizeBounds() const override { static_assert(SectorSize <= std::numeric_limits::max(), "Incorrect sector size"); static_assert(SectorSize >= std::numeric_limits::min(), "Incorrect sector size"); @@ -31,6 +30,8 @@ private: uint64_t cachePos = 0; std::chrono::time_point cachedAt; + Access getPossibleAccess() const override { return Access::EntireCard; } + optional readLogicalDiskAligned(Communication& com, device_eventhandler_t report, uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout) override; }; diff --git a/test/diskdrivertest.h b/test/diskdrivertest.h index f120a1d..585f428 100644 --- a/test/diskdrivertest.h +++ b/test/diskdrivertest.h @@ -15,7 +15,6 @@ using namespace icsneo; class MockDiskDriver : public Disk::ReadDriver, public Disk::WriteDriver { public: - Disk::Access getAccess() const override { return Disk::Access::EntireCard; } std::pair getBlockSizeBounds() const override { return { 8, 256 }; } optional readLogicalDiskAligned(Communication&, device_eventhandler_t, @@ -74,6 +73,9 @@ public: size_t atomicityChecks = 0; bool supportsAtomic = true; // Ability to simulate a driver that doesn't support atomic writes std::function afterReadHook; + +private: + Disk::Access getPossibleAccess() const override { return Disk::Access::EntireCard; } }; class DiskDriverTest : public ::testing::Test {