Add pattern write and verify to erase
The goals are to write a non-compressible and verifiable pattern to the
drive as a means to validate that the drive is working, and ensure all
blocks have been overwritten.
Tested:
$ systemctl stop emmc.service
$ ./eStoraged -b /dev/mmcblk0&
$busctl call xyz.openbmc_project.eStoraged.mmcblk0 /xyz/openbmc_project/storage/mmcblk0 xyz.openbmc_project.Inventory.Item.Volume Erase s xyz.openbmc_project.Inventory.Item.Volume.EraseMethod.LogicalOverWrite --timeout=1200
$busctl call xyz.openbmc_project.eStoraged.mmcblk0 /xyz/openbmc_project/storage/mmcblk0 xyz.openbmc_project.Inventory.Item.Volume Erase s xyz.openbmc_project.Inventory.Item.Volume.EraseMethod.LogicalVerify --timeout=1200
$echo "jebr" > /dev/mmcblk0
$busctl call xyz.openbmc_project.eStoraged.mmcblk0 /xyz/openbmc_project/storage/mmcblk0 xyz.openbmc_project.Inventory.Item.Volume Erase s xyz.openbmc_project.Inventory.Item.Volume.EraseMethod.LogicalVerify --timeout=1200
Call failed: The operation failed internally.
Change-Id: Ibc1254279b1f46246eb37056ea6e4e1a57159bb9
Signed-off-by: John Edward Broadbent <jebr@google.com>
diff --git a/include/erase.hpp b/include/erase.hpp
index 699d430..6d5644c 100644
--- a/include/erase.hpp
+++ b/include/erase.hpp
@@ -1,3 +1,4 @@
+#pragma once
#include <string>
/** @class Erase
@@ -11,6 +12,7 @@
*/
Erase(std::string_view inDevPath) : devPath(inDevPath)
{}
+ virtual ~Erase() = default;
/** @brief finds the size of the linux block device in bytes
* @return size of a block device using the devPath
diff --git a/include/pattern.hpp b/include/pattern.hpp
new file mode 100644
index 0000000..8c5b7d1
--- /dev/null
+++ b/include/pattern.hpp
@@ -0,0 +1,38 @@
+#pragma once
+
+#include "erase.hpp"
+
+#include <stdplus/fd/create.hpp>
+#include <stdplus/fd/managed.hpp>
+
+#include <span>
+#include <string>
+
+using stdplus::fd::ManagedFd;
+
+class Pattern : public Erase
+{
+ public:
+ /** @brief Creates a pattern erase object.
+ *
+ * @param[in] inDevPath - the linux device path for the block device.
+ */
+ Pattern(std::string_view inDevPath) : Erase(inDevPath)
+ {}
+
+ /** @brief writes an uncompressible random pattern to the drive
+ * and throws errors accordingly.
+ *
+ * @param[in] bytes - Size of the block device
+ * @param[in] managedFd - the file descriptor for the open drive
+ */
+ void writePattern(uint64_t driveSize, ManagedFd& fd);
+
+ /** @brief verifies the uncompressible random pattern is on the drive
+ * and throws errors accordingly.
+ *
+ * @param[in] bytes - Size of the block device
+ * @param[in] managedFd - the file descriptor for the open drive
+ */
+ void verifyPattern(uint64_t driveSize, ManagedFd& fd);
+};
diff --git a/src/erase/meson.build b/src/erase/meson.build
index 6aaf3a3..7dbd727 100644
--- a/src/erase/meson.build
+++ b/src/erase/meson.build
@@ -1,6 +1,7 @@
libeStoragedErase_lib = static_library(
'libeStoragedErase-lib',
'verifyDriveGeometry.cpp',
+ 'pattern.cpp',
'erase.cpp',
include_directories : eStoraged_headers,
implicit_include_directories: false,
diff --git a/src/erase/pattern.cpp b/src/erase/pattern.cpp
new file mode 100644
index 0000000..649bd37
--- /dev/null
+++ b/src/erase/pattern.cpp
@@ -0,0 +1,99 @@
+#include "pattern.hpp"
+
+#include "erase.hpp"
+
+#include <unistd.h>
+
+#include <phosphor-logging/lg2.hpp>
+#include <stdplus/fd/create.hpp>
+#include <stdplus/fd/managed.hpp>
+#include <xyz/openbmc_project/Common/error.hpp>
+
+#include <array>
+#include <iostream>
+#include <random>
+#include <span>
+#include <string>
+
+constexpr uint32_t seed = 0x6a656272;
+constexpr size_t blockSize = 4096;
+constexpr size_t blockSizeUsing32 = blockSize / sizeof(uint32_t);
+
+using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
+using stdplus::fd::ManagedFd;
+
+void Pattern::writePattern(const uint64_t driveSize, ManagedFd& fd)
+{
+ // static seed defines a fixed prng sequnce so it can be verified later,
+ // and validated for entropy
+ uint64_t currentIndex = 0;
+ std::minstd_rand0 generator(seed);
+ std::array<std::byte, blockSize> randArr;
+ while (currentIndex < driveSize)
+ {
+ // generate a 4k block of prng
+ std::array<uint32_t, blockSizeUsing32>* randArrFill =
+ reinterpret_cast<std::array<uint32_t, blockSizeUsing32>*>(&randArr);
+ for (uint32_t i = 0; i < blockSizeUsing32; i++)
+ {
+ (*randArrFill)[i] = generator();
+ }
+ // if we can write all 4k bytes do that, else write the remainder
+ size_t writeSize = currentIndex + blockSize < driveSize
+ ? blockSize
+ : driveSize - currentIndex;
+ if (fd.write({randArr.data(), writeSize}).size() != writeSize)
+ {
+ lg2::error("Estoraged erase pattern unable to write sizeof(long)",
+ "REDFISH_MESSAGE_ID",
+ std::string("eStorageD.1.0.EraseFailure"),
+ "REDFISH_MESSAGE_ARGS", std::to_string(fd.get()));
+ throw InternalFailure();
+ }
+ currentIndex = currentIndex + writeSize;
+ }
+}
+
+void Pattern::verifyPattern(const uint64_t driveSize, ManagedFd& fd)
+{
+
+ uint64_t currentIndex = 0;
+ std::minstd_rand0 generator(seed);
+ std::array<std::byte, blockSize> randArr;
+ std::array<std::byte, blockSize> readArr;
+ while (currentIndex < driveSize)
+ {
+ size_t readSize = currentIndex + blockSize < driveSize
+ ? blockSize
+ : driveSize - currentIndex;
+ try
+ {
+ std::array<uint32_t, blockSizeUsing32>* randArrFill =
+ reinterpret_cast<std::array<uint32_t, blockSizeUsing32>*>(
+ &randArr);
+ for (uint32_t i = 0; i < blockSizeUsing32; i++)
+ {
+ (*randArrFill)[i] = generator();
+ }
+ fd.read({readArr.data(), readSize});
+ }
+ catch (...)
+ {
+ lg2::error("Estoraged erase pattern unable to read",
+ "REDFISH_MESSAGE_ID",
+ std::string("eStorageD.1.0.EraseFailure"),
+ "REDFISH_MESSAGE_ARGS", std::to_string(fd.get()));
+ throw InternalFailure();
+ }
+
+ if (!std::equal(randArr.begin(), randArr.begin() + readSize,
+ readArr.begin(), readArr.begin() + readSize))
+ {
+ lg2::error("Estoraged erase pattern does not match",
+ "REDFISH_MESSAGE_ID",
+ std::string("eStorageD.1.0.EraseFailure"));
+ throw InternalFailure();
+ }
+ currentIndex = currentIndex + readSize;
+ }
+}
diff --git a/src/estoraged.cpp b/src/estoraged.cpp
index 91a7f12..777232a 100644
--- a/src/estoraged.cpp
+++ b/src/estoraged.cpp
@@ -2,6 +2,7 @@
#include "estoraged.hpp"
#include "cryptsetupInterface.hpp"
+#include "pattern.hpp"
#include "verifyDriveGeometry.hpp"
#include <libcryptsetup.h>
@@ -70,10 +71,20 @@
}
case EraseMethod::LogicalOverWrite:
{
+ Pattern myErasePattern(devPath);
+ ManagedFd drivefd =
+ stdplus::fd::open(devPath, stdplus::fd::OpenAccess::WriteOnly);
+ myErasePattern.writePattern(myErasePattern.findSizeOfBlockDevice(),
+ drivefd);
break;
}
case EraseMethod::LogicalVerify:
{
+ Pattern myErasePattern(devPath);
+ ManagedFd drivefd =
+ stdplus::fd::open(devPath, stdplus::fd::OpenAccess::ReadOnly);
+ myErasePattern.verifyPattern(myErasePattern.findSizeOfBlockDevice(),
+ drivefd);
break;
}
case EraseMethod::VendorSanitize:
diff --git a/src/test/erase/pattern_test.cpp b/src/test/erase/pattern_test.cpp
new file mode 100644
index 0000000..55e83a5
--- /dev/null
+++ b/src/test/erase/pattern_test.cpp
@@ -0,0 +1,81 @@
+#include "estoraged_conf.hpp"
+#include "pattern.hpp"
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <stdplus/fd/create.hpp>
+#include <stdplus/fd/managed.hpp>
+#include <xyz/openbmc_project/Common/error.hpp>
+
+#include <system_error>
+
+#include <gmock/gmock-matchers.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
+using stdplus::fd::ManagedFd;
+
+class MockManagedFd : public ManagedFd
+{
+ public:
+ MockManagedFd(int fd) : ManagedFd(std::move(fd))
+ {}
+ ~MockManagedFd()
+ {}
+};
+
+TEST(pattern, patternPass)
+{
+ uint64_t size = 4096;
+ int fds[2];
+ Pattern pass("fileName");
+ if (pipe(fds))
+ {
+ FAIL();
+ }
+ MockManagedFd writeFd(fds[1]);
+ EXPECT_NO_THROW(pass.writePattern(size, writeFd));
+ MockManagedFd verifyFd(fds[0]);
+ EXPECT_NO_THROW(pass.verifyPattern(size, verifyFd));
+}
+
+/* This test that pattern writes the correct number of bytes even if
+ * size of the drive is not divisable by the block size
+ */
+TEST(pattern, patternPassNotDivisible)
+{
+ uint64_t size = 4097;
+ // 4097 is not divisible by the block size, and we expect no errors
+ int fds[2];
+ Pattern pass("fileName");
+ if (pipe(fds))
+ {
+ FAIL();
+ }
+ MockManagedFd writeFd(fds[1]);
+ EXPECT_NO_THROW(pass.writePattern(size, writeFd));
+ MockManagedFd verifyFd(fds[0]);
+ EXPECT_NO_THROW(pass.verifyPattern(size, verifyFd));
+}
+
+TEST(pattern, patternsDontMatch)
+{
+ uint64_t size = 4096;
+ int fds[2];
+ Pattern pass("fileName");
+ if (pipe(fds))
+ {
+ FAIL();
+ }
+ int dummyValue = 88;
+ if (::write(fds[1], &dummyValue, sizeof(dummyValue)) != sizeof(dummyValue))
+ {
+ FAIL();
+ }
+ MockManagedFd writeFd(fds[1]);
+ pass.writePattern(size - sizeof(dummyValue), writeFd);
+ MockManagedFd verifyFd(fds[0]);
+ EXPECT_THROW(pass.verifyPattern(size, verifyFd), InternalFailure);
+}
diff --git a/src/test/meson.build b/src/test/meson.build
index 8f05182..0373840 100644
--- a/src/test/meson.build
+++ b/src/test/meson.build
@@ -3,6 +3,7 @@
tests = [
'erase/verifyGeometry_test',
+ 'erase/pattern_test',
'estoraged_test',
]