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/estoraged.hpp b/include/estoraged.hpp
index 557c490..3a18416 100644
--- a/include/estoraged.hpp
+++ b/include/estoraged.hpp
@@ -1,17 +1,27 @@
 #pragma once
 
+#include "cryptsetupInterface.hpp"
+#include "filesystemInterface.hpp"
+
+#include <libcryptsetup.h>
+
 #include <sdbusplus/bus.hpp>
 #include <sdbusplus/exception.hpp>
 #include <sdbusplus/server/object.hpp>
 #include <xyz/openbmc_project/eStoraged/server.hpp>
 
+#include <filesystem>
+#include <memory>
 #include <string>
+#include <string_view>
 #include <vector>
 
 namespace estoraged
 {
 using eStoragedInherit = sdbusplus::server::object_t<
     sdbusplus::xyz::openbmc_project::server::eStoraged>;
+using estoraged::Cryptsetup;
+using estoraged::Filesystem;
 
 /** @class eStoraged
  *  @brief eStoraged object to manage a LUKS encrypted storage device.
@@ -19,10 +29,27 @@
 class eStoraged : eStoragedInherit
 {
   public:
+    /** @brief Constructor for eStoraged
+     *
+     *  @param[in] bus - sdbusplus dbus object
+     *  @param[in] path - DBus object path
+     *  @param[in] devPath - path to device file, e.g. /dev/mmcblk0
+     *  @param[in] luksName - name for the LUKS container
+     *  @param[in] cryptInterface - (optional) pointer to CryptsetupInterface
+     *    object
+     *  @param[in] fsInterface - (optional) pointer to FilesystemInterface
+     *    object
+     */
     eStoraged(sdbusplus::bus::bus& bus, const char* path,
-              const std::string& devPath, const std::string& containerName) :
+              const std::string& devPath, const std::string& luksName,
+              std::unique_ptr<CryptsetupInterface> cryptInterface =
+                  std::make_unique<Cryptsetup>(),
+              std::unique_ptr<FilesystemInterface> fsInterface =
+                  std::make_unique<Filesystem>()) :
         eStoragedInherit(bus, path),
-        devPath(devPath), containerName(containerName)
+        devPath(devPath), containerName(luksName),
+        mountPoint("/mnt/" + luksName + "_fs"),
+        cryptIface(std::move(cryptInterface)), fsIface(std::move(fsInterface))
     {}
 
     /** @brief Format the LUKS encrypted device and create empty filesystem.
@@ -58,12 +85,65 @@
     void changePassword(std::vector<uint8_t> oldPassword,
                         std::vector<uint8_t> newPassword) override;
 
+    /** @brief Check if the LUKS device is currently locked. */
+    bool isLocked() const;
+
+    /** @brief Get the mount point for the filesystem on the LUKS device. */
+    std::string_view getMountPoint() const;
+
   private:
-    /* Full path of the device file, e.g. /dev/mmcblk0 */
+    /** @brief Full path of the device file, e.g. /dev/mmcblk0. */
     std::string devPath;
 
-    /* Name of the LUKS container. */
+    /** @brief Name of the LUKS container. */
     std::string containerName;
+
+    /** @brief Mount point for the filesystem. */
+    std::string mountPoint;
+
+    /** @brief Pointer to cryptsetup interface object.
+     *  @details This is used to mock out the cryptsetup functions.
+     */
+    std::unique_ptr<CryptsetupInterface> cryptIface;
+
+    /** @brief Pointer to filesystem interface object.
+     *  @details This is used to mock out filesystem operations.
+     */
+    std::unique_ptr<FilesystemInterface> fsIface;
+
+    /** @brief Format LUKS encrypted device.
+     *
+     *  @param[in] cd - initialized crypt_device struct for the device.
+     *  @param[in] password - password to set for the LUKS device.
+     */
+    void formatLuksDev(struct crypt_device* cd, std::vector<uint8_t> password);
+
+    /** @brief Unlock the device.
+     *
+     *  @param[in] cd - initialized crypt_device struct for the device.
+     *  @param[in] password - password to activate the LUKS device.
+     */
+    void activateLuksDev(struct crypt_device* cd,
+                         std::vector<uint8_t> password);
+
+    /** @brief Create the filesystem on the LUKS device.
+     *  @details The LUKS device should already be activated, i.e. unlocked.
+     */
+    void createFilesystem();
+
+    /** @brief Deactivate the LUKS device.
+     *  @details The filesystem is assumed to be unmounted already.
+     */
+    void deactivateLuksDev();
+
+    /** @brief Mount the filesystem.
+     *  @details The filesystem should already exist and the LUKS device should
+     *  be unlocked already.
+     */
+    void mountFilesystem();
+
+    /** @brief Unmount the filesystem. */
+    void unmountFilesystem();
 };
 
 } // namespace estoraged