Format LUKS encrypted device

This commit adds the functionality to format a new LUKS device, create a
filesystem, and mount it. Unit tests are included.

Currently, the D-Bus interface to format the LUKS device is synchronous,
but it may need to become asynchronous, since it can take some time. The
format operation took about 20 seconds when testing it.

Tested: Ran eStoraged on a machine with an eMMC, using the following
commands:
$ /usr/bin/eStoraged -b /dev/mmcblk0 &
$ busctl call xyz.openbmc_project.eStoraged.mmcblk0 \
  /xyz/openbmc_project/storage/mmcblk0 xyz.openbmc_project.eStoraged \
  Format ay 3 1 2 3
$ busctl call xyz.openbmc_project.eStoraged.mmcblk0 \
  /xyz/openbmc_project/storage/mmcblk0 xyz.openbmc_project.eStoraged \
  Lock ay 3 1 2 3
$ busctl call xyz.openbmc_project.eStoraged.mmcblk0 \
  /xyz/openbmc_project/storage/mmcblk0 xyz.openbmc_project.eStoraged \
  Unlock ay 3 1 2 3

Signed-off-by: John Wedig <johnwedig@google.com>
Change-Id: Ib5d0b8bb201b43a60238bfd4f13a29a6519a9f7d
diff --git a/include/filesystemInterface.hpp b/include/filesystemInterface.hpp
new file mode 100644
index 0000000..d34ff06
--- /dev/null
+++ b/include/filesystemInterface.hpp
@@ -0,0 +1,111 @@
+#pragma once
+
+#include <sys/mount.h>
+
+#include <filesystem>
+#include <string>
+
+namespace estoraged
+{
+
+/** @class FilesystemInterface
+ *  @brief Interface to the filesystem operations that eStoraged needs.
+ *  @details This class is used to mock out the filesystem operations.
+ */
+class FilesystemInterface
+{
+  public:
+    virtual ~FilesystemInterface() = default;
+
+    /** @brief Runs the mkfs command to create the filesystem.
+     *  @details Used for mocking purposes.
+     *
+     *  @param[in] logicalVolume - name of the mapped LUKS device.
+     *
+     *  @returns 0 on success, nonzero on failure.
+     */
+    virtual int runMkfs(const std::string& logicalVolume) = 0;
+
+    /** @brief Wrapper around mount().
+     *  @details Used for mocking purposes.
+     *
+     *  @param[in] source - device where the filesystem is located.
+     *  @param[in] target - path to where the filesystem should be mounted.
+     *  @param[in] filesystemType - (e.g. "ext4").
+     *  @param[in] mountflags - flags bit mask (see mount() documentation).
+     *  @param[in] data - options for specific filesystem type, can be NULL
+     *    (see mount() documentation).
+     *
+     *  @returns On success, zero is returned.  On error, -1 is returned, and
+     *    errno is set to indicate the error.
+     */
+    virtual int doMount(const char* source, const char* target,
+                        const char* filesystemtype, unsigned long mountflags,
+                        const void* data) = 0;
+
+    /** @brief Wrapper around umount().
+     *  @details Used for mocking purposes.
+     *
+     *  @param[in] target - path location where the filesystem is mounted.
+     *
+     *  @returns On success, zero is returned.  On error, -1 is returned, and
+     *    errno is set to indicate the error.
+     */
+    virtual int doUnmount(const char* target) = 0;
+
+    /** @brief Wrapper around std::filesystem::create_directory.
+     *  @details Used for mocking purposes.
+     *
+     *  @param[in] p - path to directory that should be created.
+     *
+     *  @returns true on success, false otherwise.
+     */
+    virtual bool createDirectory(const std::filesystem::path& p) = 0;
+
+    /** @brief Wrapper around std::filesystem::remove.
+     *  @details Used for mocking purposes.
+     *
+     *  @param[in] p - path to directory that should be removed.
+     *
+     *  @returns true on success, false otherwise.
+     */
+    virtual bool removeDirectory(const std::filesystem::path& p) = 0;
+};
+
+/** @class Filesystem
+ *  @brief Implements FilesystemInterface
+ */
+class Filesystem : public FilesystemInterface
+{
+  public:
+    ~Filesystem() = default;
+
+    int runMkfs(const std::string& logicalVolume) override
+    {
+        std::string mkfsCommand("mkfs.ext4 /dev/mapper/" + logicalVolume);
+        return system(mkfsCommand.c_str());
+    }
+
+    int doMount(const char* source, const char* target,
+                const char* filesystemtype, unsigned long mountflags,
+                const void* data) override
+    {
+        return mount(source, target, filesystemtype, mountflags, data);
+    }
+
+    int doUnmount(const char* target) override
+    {
+        return umount(target);
+    }
+
+    bool createDirectory(const std::filesystem::path& p) override
+    {
+        return std::filesystem::create_directory(p);
+    }
+
+    bool removeDirectory(const std::filesystem::path& p) override
+    {
+        return std::filesystem::remove(p);
+    }
+};
+} // namespace estoraged