Implement buildFileTable interface

The buildFileTable interface will be used by the ReadFileIntoMemory
and WriteFileFromMemory commands to get the file table and map the
file handle to the file path.

Change-Id: Ibdc01737d8925ef918c4fbe2919108d96a2807ab
Signed-off-by: Tom Joseph <tomjoseph@in.ibm.com>
diff --git a/configure.ac b/configure.ac
index 53d9228..ae5fcba 100644
--- a/configure.ac
+++ b/configure.ac
@@ -44,6 +44,8 @@
     AC_SUBST([OESDK_TESTCASE_FLAGS], [$testcase_flags])
 )
 
+AC_DEFINE(FILE_TABLE_JSON, "/var/lib/pldm/fileTable.json", [JSON file containing file info for File I/O])
+
 # Create configured output
 AC_CONFIG_FILES([Makefile libpldm/Makefile libpldmresponder/Makefile test/Makefile])
 AC_OUTPUT
diff --git a/libpldmresponder/file_io.cpp b/libpldmresponder/file_io.cpp
index 9cf093c..50c3f1d 100644
--- a/libpldmresponder/file_io.cpp
+++ b/libpldmresponder/file_io.cpp
@@ -1,5 +1,9 @@
+#include "config.h"
+
 #include "file_io.hpp"
 
+#include "file_table.hpp"
+
 #include <fcntl.h>
 #include <sys/mman.h>
 #include <sys/stat.h>
@@ -132,7 +136,6 @@
     uint32_t offset = 0;
     uint32_t length = 0;
     uint64_t address = 0;
-    fs::path path("");
 
     Response response((sizeof(pldm_msg_hdr) + PLDM_RW_FILE_MEM_RESP_BYTES), 0);
     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
@@ -147,7 +150,24 @@
     decode_rw_file_memory_req(request, payloadLength, &fileHandle, &offset,
                               &length, &address);
 
-    if (!fs::exists(path))
+    using namespace pldm::filetable;
+    auto& table = buildFileTable(FILE_TABLE_JSON);
+    FileEntry value{};
+
+    try
+    {
+        value = table.at(fileHandle);
+    }
+    catch (std::exception& e)
+    {
+        log<level::ERR>("File handle does not exist in the file table",
+                        entry("HANDLE=%d", fileHandle));
+        encode_rw_file_memory_resp(0, PLDM_READ_FILE_INTO_MEMORY,
+                                   PLDM_INVALID_FILE_HANDLE, 0, responsePtr);
+        return response;
+    }
+
+    if (!fs::exists(value.fsPath))
     {
         log<level::ERR>("File does not exist", entry("HANDLE=%d", fileHandle));
         encode_rw_file_memory_resp(0, PLDM_READ_FILE_INTO_MEMORY,
@@ -155,7 +175,7 @@
         return response;
     }
 
-    auto fileSize = fs::file_size(path);
+    auto fileSize = fs::file_size(value.fsPath);
     if (offset >= fileSize)
     {
         log<level::ERR>("Offset exceeds file size", entry("OFFSET=%d", offset),
@@ -181,8 +201,8 @@
 
     using namespace dma;
     DMA intf;
-    return transferAll<DMA>(&intf, PLDM_READ_FILE_INTO_MEMORY, path, offset,
-                            length, address, true);
+    return transferAll<DMA>(&intf, PLDM_READ_FILE_INTO_MEMORY, value.fsPath,
+                            offset, length, address, true);
 }
 
 Response writeFileFromMemory(const uint8_t* request, size_t payloadLength)
@@ -191,7 +211,6 @@
     uint32_t offset = 0;
     uint32_t length = 0;
     uint64_t address = 0;
-    fs::path path("");
 
     Response response(sizeof(pldm_msg_hdr) + PLDM_RW_FILE_MEM_RESP_BYTES, 0);
     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
@@ -215,7 +234,24 @@
         return response;
     }
 
-    if (!fs::exists(path))
+    using namespace pldm::filetable;
+    auto& table = buildFileTable(FILE_TABLE_JSON);
+    FileEntry value{};
+
+    try
+    {
+        value = table.at(fileHandle);
+    }
+    catch (std::exception& e)
+    {
+        log<level::ERR>("File handle does not exist in the file table",
+                        entry("HANDLE=%d", fileHandle));
+        encode_rw_file_memory_resp(0, PLDM_WRITE_FILE_FROM_MEMORY,
+                                   PLDM_INVALID_FILE_HANDLE, 0, responsePtr);
+        return response;
+    }
+
+    if (!fs::exists(value.fsPath))
     {
         log<level::ERR>("File does not exist", entry("HANDLE=%d", fileHandle));
         encode_rw_file_memory_resp(0, PLDM_WRITE_FILE_FROM_MEMORY,
@@ -223,7 +259,7 @@
         return response;
     }
 
-    auto fileSize = fs::file_size(path);
+    auto fileSize = fs::file_size(value.fsPath);
     if (offset >= fileSize)
     {
         log<level::ERR>("Offset exceeds file size", entry("OFFSET=%d", offset),
@@ -235,8 +271,8 @@
 
     using namespace dma;
     DMA intf;
-    return transferAll<DMA>(&intf, PLDM_WRITE_FILE_FROM_MEMORY, path, offset,
-                            length, address, false);
+    return transferAll<DMA>(&intf, PLDM_WRITE_FILE_FROM_MEMORY, value.fsPath,
+                            offset, length, address, false);
 }
 
 } // namespace responder
diff --git a/libpldmresponder/file_table.cpp b/libpldmresponder/file_table.cpp
index 85f417c..9264f21 100644
--- a/libpldmresponder/file_table.cpp
+++ b/libpldmresponder/file_table.cpp
@@ -115,5 +115,15 @@
     return table;
 }
 
+FileTable& buildFileTable(const std::string& fileTablePath)
+{
+    static FileTable table;
+    if (table.isEmpty())
+    {
+        table = std::move(FileTable(fileTablePath));
+    }
+    return table;
+}
+
 } // namespace filetable
 } // namespace pldm
diff --git a/libpldmresponder/file_table.hpp b/libpldmresponder/file_table.hpp
index 385f57b..4b82f29 100644
--- a/libpldmresponder/file_table.hpp
+++ b/libpldmresponder/file_table.hpp
@@ -111,5 +111,15 @@
     uint32_t checkSum = 0;
 };
 
+/** @brief Build the file attribute table if not already built using the
+ *         file table config.
+ *
+ *  @param[in] fileTablePath - path of the file table config
+ *
+ *  @return FileTable& - Reference to instance of file table
+ */
+
+FileTable& buildFileTable(const std::string& fileTablePath);
+
 } // namespace filetable
 } // namespace pldm
diff --git a/test/libpldmresponder_fileio_test.cpp b/test/libpldmresponder_fileio_test.cpp
index 424e09c..2c5f90e 100644
--- a/test/libpldmresponder_fileio_test.cpp
+++ b/test/libpldmresponder_fileio_test.cpp
@@ -233,6 +233,118 @@
     ASSERT_EQ(responsePtr->payload[0], PLDM_ERROR_INVALID_LENGTH);
 }
 
+TEST_F(TestFileTable, ReadFileInvalidFileHandle)
+{
+    // Invalid file handle in the file table
+    uint32_t fileHandle = 2;
+    uint32_t offset = 0;
+    uint32_t length = 0;
+    uint64_t address = 0;
+
+    std::array<uint8_t, PLDM_RW_FILE_MEM_REQ_BYTES> requestMsg{};
+    memcpy(requestMsg.data(), &fileHandle, sizeof(fileHandle));
+    memcpy(requestMsg.data() + sizeof(fileHandle), &offset, sizeof(offset));
+    memcpy(requestMsg.data() + sizeof(fileHandle) + sizeof(offset), &length,
+           sizeof(length));
+    memcpy(requestMsg.data() + sizeof(fileHandle) + sizeof(offset) +
+               sizeof(length),
+           &address, sizeof(address));
+
+    using namespace pldm::filetable;
+    // Initialise the file table with 2 valid file handles 0 & 1.
+    auto& table = buildFileTable(fileTableConfig.c_str());
+
+    auto response = readFileIntoMemory(requestMsg.data(), requestMsg.size());
+    auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
+    ASSERT_EQ(responsePtr->payload[0], PLDM_INVALID_FILE_HANDLE);
+    // Clear the file table contents.
+    table.clear();
+}
+
+TEST_F(TestFileTable, ReadFileInvalidOffset)
+{
+    uint32_t fileHandle = 0;
+    // The file size is 1024, so the offset is invalid
+    uint32_t offset = 1024;
+    uint32_t length = 0;
+    uint64_t address = 0;
+
+    std::array<uint8_t, PLDM_RW_FILE_MEM_REQ_BYTES> requestMsg{};
+    memcpy(requestMsg.data(), &fileHandle, sizeof(fileHandle));
+    memcpy(requestMsg.data() + sizeof(fileHandle), &offset, sizeof(offset));
+    memcpy(requestMsg.data() + sizeof(fileHandle) + sizeof(offset), &length,
+           sizeof(length));
+    memcpy(requestMsg.data() + sizeof(fileHandle) + sizeof(offset) +
+               sizeof(length),
+           &address, sizeof(address));
+
+    using namespace pldm::filetable;
+    auto& table = buildFileTable(fileTableConfig.c_str());
+
+    auto response = readFileIntoMemory(requestMsg.data(), requestMsg.size());
+    auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
+    ASSERT_EQ(responsePtr->payload[0], PLDM_DATA_OUT_OF_RANGE);
+    // Clear the file table contents.
+    table.clear();
+}
+
+TEST_F(TestFileTable, ReadFileInvalidLength)
+{
+    uint32_t fileHandle = 0;
+    uint32_t offset = 100;
+    // Length should be a multiple of dma min size(16)
+    uint32_t length = 10;
+    uint64_t address = 0;
+
+    std::array<uint8_t, PLDM_RW_FILE_MEM_REQ_BYTES> requestMsg{};
+    memcpy(requestMsg.data(), &fileHandle, sizeof(fileHandle));
+    memcpy(requestMsg.data() + sizeof(fileHandle), &offset, sizeof(offset));
+    memcpy(requestMsg.data() + sizeof(fileHandle) + sizeof(offset), &length,
+           sizeof(length));
+    memcpy(requestMsg.data() + sizeof(fileHandle) + sizeof(offset) +
+               sizeof(length),
+           &address, sizeof(address));
+
+    using namespace pldm::filetable;
+    auto& table = buildFileTable(fileTableConfig.c_str());
+
+    auto response = readFileIntoMemory(requestMsg.data(), requestMsg.size());
+    auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
+    ASSERT_EQ(responsePtr->payload[0], PLDM_INVALID_READ_LENGTH);
+    // Clear the file table contents.
+    table.clear();
+}
+
+TEST_F(TestFileTable, ReadFileInvalidEffectiveLength)
+{
+    uint32_t fileHandle = 0;
+    // valid offset
+    uint32_t offset = 100;
+    // length + offset exceeds the size, so effective length is
+    // filesize(1024) - offset(100). The effective length is not a multiple of
+    // DMA min size(16)
+    uint32_t length = 1024;
+    uint64_t address = 0;
+
+    std::array<uint8_t, PLDM_RW_FILE_MEM_REQ_BYTES> requestMsg{};
+    memcpy(requestMsg.data(), &fileHandle, sizeof(fileHandle));
+    memcpy(requestMsg.data() + sizeof(fileHandle), &offset, sizeof(offset));
+    memcpy(requestMsg.data() + sizeof(fileHandle) + sizeof(offset), &length,
+           sizeof(length));
+    memcpy(requestMsg.data() + sizeof(fileHandle) + sizeof(offset) +
+               sizeof(length),
+           &address, sizeof(address));
+
+    using namespace pldm::filetable;
+    auto& table = buildFileTable(fileTableConfig.c_str());
+
+    auto response = readFileIntoMemory(requestMsg.data(), requestMsg.size());
+    auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
+    ASSERT_EQ(responsePtr->payload[0], PLDM_INVALID_READ_LENGTH);
+    // Clear the file table contents.
+    table.clear();
+}
+
 TEST(WriteFileFromMemory, BadPath)
 {
     uint32_t fileHandle = 0;
@@ -260,6 +372,62 @@
     ASSERT_EQ(responsePtr->payload[0], PLDM_INVALID_WRITE_LENGTH);
 }
 
+TEST_F(TestFileTable, WriteFileInvalidFileHandle)
+{
+    // Invalid file handle in the file table
+    uint32_t fileHandle = 2;
+    uint32_t offset = 0;
+    uint32_t length = 16;
+    uint64_t address = 0;
+
+    std::array<uint8_t, PLDM_RW_FILE_MEM_REQ_BYTES> requestMsg{};
+    memcpy(requestMsg.data(), &fileHandle, sizeof(fileHandle));
+    memcpy(requestMsg.data() + sizeof(fileHandle), &offset, sizeof(offset));
+    memcpy(requestMsg.data() + sizeof(fileHandle) + sizeof(offset), &length,
+           sizeof(length));
+    memcpy(requestMsg.data() + sizeof(fileHandle) + sizeof(offset) +
+               sizeof(length),
+           &address, sizeof(address));
+
+    using namespace pldm::filetable;
+    // Initialise the file table with 2 valid file handles 0 & 1.
+    auto& table = buildFileTable(fileTableConfig.c_str());
+
+    auto response = writeFileFromMemory(requestMsg.data(), requestMsg.size());
+    auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
+    ASSERT_EQ(responsePtr->payload[0], PLDM_INVALID_FILE_HANDLE);
+    // Clear the file table contents.
+    table.clear();
+}
+
+TEST_F(TestFileTable, WriteFileInvalidOffset)
+{
+    uint32_t fileHandle = 0;
+    // The file size is 1024, so the offset is invalid
+    uint32_t offset = 1024;
+    uint32_t length = 16;
+    uint64_t address = 0;
+
+    std::array<uint8_t, PLDM_RW_FILE_MEM_REQ_BYTES> requestMsg{};
+    memcpy(requestMsg.data(), &fileHandle, sizeof(fileHandle));
+    memcpy(requestMsg.data() + sizeof(fileHandle), &offset, sizeof(offset));
+    memcpy(requestMsg.data() + sizeof(fileHandle) + sizeof(offset), &length,
+           sizeof(length));
+    memcpy(requestMsg.data() + sizeof(fileHandle) + sizeof(offset) +
+               sizeof(length),
+           &address, sizeof(address));
+
+    using namespace pldm::filetable;
+    // Initialise the file table with 2 valid file handles 0 & 1.
+    auto& table = buildFileTable(TestFileTable::fileTableConfig.c_str());
+
+    auto response = writeFileFromMemory(requestMsg.data(), requestMsg.size());
+    auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
+    ASSERT_EQ(responsePtr->payload[0], PLDM_DATA_OUT_OF_RANGE);
+    // Clear the file table contents.
+    table.clear();
+}
+
 TEST(FileTable, ConfigNotExist)
 {
     logs.clear();