extend file_handler to support reads

Problem: the upcomming version handler will need to read from files.
Currently file hander (image handler) does not support reads.

Solution: Add read support by providing an optional mode parameter.

Tests added:
FileHanderTest added to
- Test out open for reads
- Test out reading out bytes and verifying size and content
- Test out trying to read beyond EOF
- Test out offset reads that go beyond EOF

Tested:
Existing unit tests pass
New unit tests for reading all pass

Signed-off-by: Jason Ling <jasonling@google.com>
Change-Id: Ie416a6b4b452d8d04fa158bd55989d07a891896f
diff --git a/bmc/file_handler.cpp b/bmc/file_handler.cpp
index f7878ea..dacca19 100644
--- a/bmc/file_handler.cpp
+++ b/bmc/file_handler.cpp
@@ -18,6 +18,7 @@
 
 #include <cstdint>
 #include <filesystem>
+#include <ios>
 #include <memory>
 #include <string>
 #include <vector>
@@ -26,8 +27,10 @@
 {
 namespace fs = std::filesystem;
 
-bool FileHandler::open(const std::string& path)
+bool FileHandler::open(const std::string& path, std::ios_base::openmode mode)
 {
+    /* force binary mode */
+    mode |= std::ios::binary;
     this->path = path;
 
     if (file.is_open())
@@ -38,9 +41,8 @@
         return false;
     }
 
-    /* using ofstream no need to set out */
-    file.open(filename, std::ios::binary);
-    if (file.bad())
+    file.open(filename, mode);
+    if (!file.good()) /* on success goodbit is set */
     {
         /* TODO: Oh no! Care about this. */
         return false;
@@ -90,6 +92,46 @@
     return true;
 }
 
+std::optional<std::vector<uint8_t>> FileHandler::read(std::uint32_t offset,
+                                                      std::uint32_t size)
+{
+    if (!file.is_open())
+    {
+        return std::nullopt;
+    }
+
+    /* determine size of file */
+    file.seekg(0, std::ios_base::end);
+    uint32_t filesize = file.tellg();
+    uint32_t bytesToRead = size;
+
+    /* make sure to not read past the end of file */
+    if (offset + size > filesize)
+    {
+        bytesToRead = filesize - offset;
+    }
+
+    /* if no bytes can be read, fail */
+    if (0 == bytesToRead)
+    {
+        return std::nullopt;
+    }
+
+    /* seek to offset then read */
+    file.seekg(offset);
+    std::vector<uint8_t> fileData(bytesToRead);
+    file.read(reinterpret_cast<char*>(fileData.data()), bytesToRead);
+
+    /* if any sort of failure happened during all the seeks
+     * and reads then fail the entire operation
+     */
+    if (!file.good())
+    {
+        return std::nullopt;
+    }
+    return fileData;
+}
+
 int FileHandler::getSize()
 {
     try