oem-ibm: add readFileByType handler

Add a handler for file type 'LID', to be able to read LID files over the
LPC channel.

Signed-off-by: Deepak Kodihalli <dkodihal@in.ibm.com>
Change-Id: I9db77f78b0aa4e289fb0ab940121569b431f4dfe
diff --git a/oem/ibm/libpldmresponder/file_io.cpp b/oem/ibm/libpldmresponder/file_io.cpp
index 0c7216b..05051b6 100644
--- a/oem/ibm/libpldmresponder/file_io.cpp
+++ b/oem/ibm/libpldmresponder/file_io.cpp
@@ -47,6 +47,8 @@
                     std::move(writeFileByTypeFromMemory));
     registerHandler(PLDM_OEM, PLDM_READ_FILE_BY_TYPE_INTO_MEMORY,
                     std::move(readFileByTypeIntoMemory));
+    registerHandler(PLDM_OEM, PLDM_READ_FILE_BY_TYPE,
+                    std::move(readFileByType));
 }
 
 } // namespace oem_ibm
@@ -612,5 +614,52 @@
                                   payloadLength);
 }
 
+Response readFileByType(const pldm_msg* request, size_t payloadLength)
+{
+    Response response(sizeof(pldm_msg_hdr) + PLDM_RW_FILE_BY_TYPE_RESP_BYTES);
+    auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
+
+    if (payloadLength != PLDM_RW_FILE_BY_TYPE_REQ_BYTES)
+    {
+        encode_rw_file_by_type_resp(request->hdr.instance_id,
+                                    PLDM_READ_FILE_BY_TYPE,
+                                    PLDM_ERROR_INVALID_LENGTH, 0, responsePtr);
+        return response;
+    }
+    uint16_t fileType{};
+    uint32_t fileHandle{};
+    uint32_t offset{};
+    uint32_t length{};
+
+    auto rc = decode_rw_file_by_type_req(request, payloadLength, &fileType,
+                                         &fileHandle, &offset, &length);
+    if (rc != PLDM_SUCCESS)
+    {
+        encode_rw_file_by_type_resp(request->hdr.instance_id,
+                                    PLDM_READ_FILE_BY_TYPE, rc, 0, responsePtr);
+        return response;
+    }
+
+    std::unique_ptr<FileHandler> handler{};
+    try
+    {
+        handler = getHandlerByType(fileType, fileHandle);
+    }
+    catch (const InternalFailure& e)
+    {
+        log<level::ERR>("unknown file type", entry("TYPE=%d", fileType));
+        encode_rw_file_by_type_resp(request->hdr.instance_id,
+                                    PLDM_READ_FILE_BY_TYPE,
+                                    PLDM_INVALID_FILE_TYPE, 0, responsePtr);
+        return response;
+    }
+
+    rc = handler->read(offset, length, response);
+    encode_rw_file_by_type_resp(request->hdr.instance_id,
+                                PLDM_READ_FILE_BY_TYPE, rc, length,
+                                responsePtr);
+    return response;
+}
+
 } // namespace responder
 } // namespace pldm
diff --git a/oem/ibm/libpldmresponder/file_io.hpp b/oem/ibm/libpldmresponder/file_io.hpp
index b67f0f9..07a13aa 100644
--- a/oem/ibm/libpldmresponder/file_io.hpp
+++ b/oem/ibm/libpldmresponder/file_io.hpp
@@ -160,6 +160,15 @@
 Response readFileByTypeIntoMemory(const pldm_msg* request,
                                   size_t payloadLength);
 
+/** @brief Handler for readFileByType command
+ *
+ *  @param[in] request - pointer to PLDM request payload
+ *  @param[in] payloadLength - length of the message
+ *
+ *  @return PLDM response message
+ */
+Response readFileByType(const pldm_msg* request, size_t payloadLength);
+
 /** @brief Handler for GetFileTable command
  *
  *  @param[in] request - pointer to PLDM request payload
diff --git a/oem/ibm/libpldmresponder/file_io_by_type.cpp b/oem/ibm/libpldmresponder/file_io_by_type.cpp
index 152efb9..efe8e37 100644
--- a/oem/ibm/libpldmresponder/file_io_by_type.cpp
+++ b/oem/ibm/libpldmresponder/file_io_by_type.cpp
@@ -12,6 +12,7 @@
 
 #include <exception>
 #include <filesystem>
+#include <fstream>
 #include <phosphor-logging/elog-errors.hpp>
 #include <phosphor-logging/log.hpp>
 #include <vector>
@@ -29,7 +30,7 @@
 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
 
 int FileHandler::transferFileData(const fs::path& path, bool upstream,
-                                  uint32_t offset, uint32_t length,
+                                  uint32_t offset, uint32_t& length,
                                   uint64_t address)
 {
     if (upstream)
@@ -103,5 +104,43 @@
     return nullptr;
 }
 
+int FileHandler::readFile(const std::string& filePath, uint32_t offset,
+                          uint32_t& length, Response& response)
+{
+    if (!fs::exists(filePath))
+    {
+        log<level::ERR>("File does not exist", entry("HANDLE=%d", fileHandle),
+                        entry("PATH=%s", filePath.c_str()));
+        return PLDM_INVALID_FILE_HANDLE;
+    }
+
+    size_t fileSize = fs::file_size(filePath);
+    if (offset >= fileSize)
+    {
+        log<level::ERR>("Offset exceeds file size", entry("OFFSET=%d", offset),
+                        entry("FILE_SIZE=%d", fileSize));
+        return PLDM_DATA_OUT_OF_RANGE;
+    }
+
+    if (offset + length > fileSize)
+    {
+        length = fileSize - offset;
+    }
+
+    size_t currSize = response.size();
+    response.resize(currSize + length);
+    auto filePos = reinterpret_cast<char*>(response.data());
+    filePos += currSize;
+    std::ifstream stream(filePath, std::ios::in | std::ios::binary);
+    if (stream)
+    {
+        stream.seekg(offset);
+        stream.read(filePos, length);
+        return PLDM_SUCCESS;
+    }
+    log<level::ERR>("Unable to read file", entry("FILE=%s", filePath.c_str()));
+    return PLDM_ERROR;
+}
+
 } // namespace responder
 } // namespace pldm
diff --git a/oem/ibm/libpldmresponder/file_io_by_type.hpp b/oem/ibm/libpldmresponder/file_io_by_type.hpp
index f39650d..779eaef 100644
--- a/oem/ibm/libpldmresponder/file_io_by_type.hpp
+++ b/oem/ibm/libpldmresponder/file_io_by_type.hpp
@@ -33,13 +33,31 @@
      *  file types need to override this method to do the file specific
      *  processing
      *  @param[in] offset - offset to read
-     *  @param[in] length - length to be read mentioned by Host
+     *  @param[in/out] length - length to be read mentioned by Host
      *  @param[in] address - DMA address
      *  @return PLDM status code
      */
-    virtual int readIntoMemory(uint32_t offset, uint32_t length,
+    virtual int readIntoMemory(uint32_t offset, uint32_t& length,
                                uint64_t address) = 0;
 
+    /** @brief Method to read an oem file type's content into the PLDM response.
+     *  @param[in] offset - offset to read
+     *  @param[in/out] length - length to be read
+     *  @param[in] response - PLDM response
+     *  @return PLDM status code
+     */
+    virtual int read(uint32_t offset, uint32_t& length, Response& response) = 0;
+
+    /** @brief Method to read an oem file type's content into the PLDM response.
+     *  @param[in] filePath - file to read from
+     *  @param[in] offset - offset to read
+     *  @param[in/out] length - length to be read
+     *  @param[in] response - PLDM response
+     *  @return PLDM status code
+     */
+    virtual int readFile(const std::string& filePath, uint32_t offset,
+                         uint32_t& length, Response& response);
+
     /** @brief Method to do the file content transfer ove DMA between host and
      *  bmc. This method is made virtual to be overridden in test case. And need
      *  not be defined in other child classes
@@ -48,13 +66,13 @@
      *  @param[in] upstream - direction of DMA transfer. "false" means a
      *                        transfer from host to BMC
      *  @param[in] offset - offset to read/write
-     *  @param[in] length - length to be read/write mentioned by Host
+     *  @param[in/out] length - length to be read/write mentioned by Host
      *  @param[in] address - DMA address
      *
      *  @return PLDM status code
      */
     virtual int transferFileData(const fs::path& path, bool upstream,
-                                 uint32_t offset, uint32_t length,
+                                 uint32_t offset, uint32_t& length,
                                  uint64_t address);
 
     /** @brief Constructor to create a FileHandler object
diff --git a/oem/ibm/libpldmresponder/file_io_type_lid.hpp b/oem/ibm/libpldmresponder/file_io_type_lid.hpp
index 45d4f73..d3a48a5 100644
--- a/oem/ibm/libpldmresponder/file_io_type_lid.hpp
+++ b/oem/ibm/libpldmresponder/file_io_type_lid.hpp
@@ -38,12 +38,17 @@
         return PLDM_ERROR_UNSUPPORTED_PLDM_CMD;
     }
 
-    virtual int readIntoMemory(uint32_t offset, uint32_t length,
+    virtual int readIntoMemory(uint32_t offset, uint32_t& length,
                                uint64_t address)
     {
         return transferFileData(lidPath, true, offset, length, address);
     }
 
+    virtual int read(uint32_t offset, uint32_t& length, Response& response)
+    {
+        return readFile(lidPath, offset, length, response);
+    }
+
     /** @brief LidHandler destructor
      */
     ~LidHandler()
diff --git a/oem/ibm/libpldmresponder/file_io_type_pel.cpp b/oem/ibm/libpldmresponder/file_io_type_pel.cpp
index 014511c..01919b9 100644
--- a/oem/ibm/libpldmresponder/file_io_type_pel.cpp
+++ b/oem/ibm/libpldmresponder/file_io_type_pel.cpp
@@ -25,12 +25,18 @@
 
 using namespace phosphor::logging;
 
-int PelHandler::readIntoMemory(uint32_t /*offset*/, uint32_t /*length*/,
+int PelHandler::readIntoMemory(uint32_t /*offset*/, uint32_t& /*length*/,
                                uint64_t /*address*/)
 {
     return PLDM_ERROR_UNSUPPORTED_PLDM_CMD;
 }
 
+int PelHandler::read(uint32_t /*offset*/, uint32_t& /*length*/,
+                     Response& /*response*/)
+{
+    return PLDM_ERROR_UNSUPPORTED_PLDM_CMD;
+}
+
 int PelHandler::writeFromMemory(uint32_t offset, uint32_t length,
                                 uint64_t address)
 {
diff --git a/oem/ibm/libpldmresponder/file_io_type_pel.hpp b/oem/ibm/libpldmresponder/file_io_type_pel.hpp
index 4c79452..d2fb5cc 100644
--- a/oem/ibm/libpldmresponder/file_io_type_pel.hpp
+++ b/oem/ibm/libpldmresponder/file_io_type_pel.hpp
@@ -25,8 +25,9 @@
 
     virtual int writeFromMemory(uint32_t offset, uint32_t length,
                                 uint64_t address);
-    virtual int readIntoMemory(uint32_t offset, uint32_t length,
+    virtual int readIntoMemory(uint32_t offset, uint32_t& length,
                                uint64_t address);
+    virtual int read(uint32_t offset, uint32_t& length, Response& response);
 
     /** @brief method to store a pel file in tempfs and send
      *  d-bus notification to pel daemon that it is ready for consumption
diff --git a/oem/ibm/test/libpldmresponder_fileio_test.cpp b/oem/ibm/test/libpldmresponder_fileio_test.cpp
index 603c0c6..3894ff3 100644
--- a/oem/ibm/test/libpldmresponder_fileio_test.cpp
+++ b/oem/ibm/test/libpldmresponder_fileio_test.cpp
@@ -1,5 +1,6 @@
 #include "libpldmresponder/file_io.hpp"
 #include "libpldmresponder/file_io_by_type.hpp"
+#include "libpldmresponder/file_io_type_lid.hpp"
 #include "libpldmresponder/file_io_type_pel.hpp"
 #include "libpldmresponder/file_table.hpp"
 #include "xyz/openbmc_project/Common/error.hpp"
@@ -763,6 +764,103 @@
     auto pelType = dynamic_cast<PelHandler*>(handler.get());
     ASSERT_TRUE(pelType != nullptr);
 
+    handler = getHandlerByType(PLDM_FILE_TYPE_LID_PERM, fileHandle);
+    auto lidType = dynamic_cast<LidHandler*>(handler.get());
+    ASSERT_TRUE(lidType != nullptr);
+    pelType = dynamic_cast<PelHandler*>(handler.get());
+    ASSERT_TRUE(pelType == nullptr);
+    handler = getHandlerByType(PLDM_FILE_TYPE_LID_TEMP, fileHandle);
+    lidType = dynamic_cast<LidHandler*>(handler.get());
+    ASSERT_TRUE(lidType != nullptr);
+
     using namespace sdbusplus::xyz::openbmc_project::Common::Error;
     ASSERT_THROW(getHandlerByType(0xFFFF, fileHandle), InternalFailure);
 }
+
+TEST(readFileByTypeIntoMemory, testBadPath)
+{
+    const auto hdr_size = sizeof(pldm_msg_hdr);
+    std::array<uint8_t, hdr_size + PLDM_RW_FILE_BY_TYPE_MEM_REQ_BYTES>
+        requestMsg{};
+    auto req = reinterpret_cast<pldm_msg*>(requestMsg.data());
+    struct pldm_read_write_file_by_type_memory_req* request =
+        reinterpret_cast<struct pldm_read_write_file_by_type_memory_req*>(
+            req->payload);
+    request->file_type = 0xFFFF;
+    request->file_handle = 0;
+    request->offset = 0;
+    request->length = 17;
+    request->address = 0;
+
+    auto response = readFileByTypeIntoMemory(req, 0);
+    auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
+    struct pldm_read_write_file_by_type_memory_resp* resp =
+        reinterpret_cast<struct pldm_read_write_file_by_type_memory_resp*>(
+            responsePtr->payload);
+    ASSERT_EQ(PLDM_ERROR_INVALID_LENGTH, resp->completion_code);
+
+    response =
+        readFileByTypeIntoMemory(req, PLDM_RW_FILE_BY_TYPE_MEM_REQ_BYTES);
+    responsePtr = reinterpret_cast<pldm_msg*>(response.data());
+    resp = reinterpret_cast<struct pldm_read_write_file_by_type_memory_resp*>(
+        responsePtr->payload);
+    ASSERT_EQ(PLDM_INVALID_WRITE_LENGTH, resp->completion_code);
+
+    request->length = 16;
+    response =
+        readFileByTypeIntoMemory(req, PLDM_RW_FILE_BY_TYPE_MEM_REQ_BYTES);
+    responsePtr = reinterpret_cast<pldm_msg*>(response.data());
+    resp = reinterpret_cast<struct pldm_read_write_file_by_type_memory_resp*>(
+        responsePtr->payload);
+    ASSERT_EQ(PLDM_INVALID_FILE_TYPE, resp->completion_code);
+}
+
+TEST(readFileByType, testBadPath)
+{
+    const auto hdr_size = sizeof(pldm_msg_hdr);
+    std::array<uint8_t, hdr_size + PLDM_RW_FILE_BY_TYPE_REQ_BYTES> requestMsg{};
+    auto payloadLength = requestMsg.size() - hdr_size;
+    auto req = reinterpret_cast<pldm_msg*>(requestMsg.data());
+    struct pldm_read_write_file_by_type_req* request =
+        reinterpret_cast<struct pldm_read_write_file_by_type_req*>(
+            req->payload);
+    request->file_type = 0xFFFF;
+    request->file_handle = 0;
+    request->offset = 0;
+    request->length = 13;
+
+    auto response = readFileByType(req, 0);
+    auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
+    struct pldm_read_write_file_by_type_resp* resp =
+        reinterpret_cast<struct pldm_read_write_file_by_type_resp*>(
+            responsePtr->payload);
+    ASSERT_EQ(PLDM_ERROR_INVALID_LENGTH, resp->completion_code);
+
+    response = readFileByType(req, payloadLength);
+    responsePtr = reinterpret_cast<pldm_msg*>(response.data());
+    resp = reinterpret_cast<struct pldm_read_write_file_by_type_resp*>(
+        responsePtr->payload);
+    ASSERT_EQ(PLDM_INVALID_FILE_TYPE, resp->completion_code);
+}
+
+TEST(readFileByType, testReadFile)
+{
+    LidHandler handler(0, true);
+    Response response;
+    uint32_t length{};
+
+    auto rc = handler.readFile({}, 0, length, response);
+    ASSERT_EQ(PLDM_INVALID_FILE_HANDLE, rc);
+
+    char tmplt[] = "/tmp/lid.XXXXXX";
+    auto fd = mkstemp(tmplt);
+    std::vector<uint8_t> in = {100, 10, 56, 78, 34, 56, 79, 235, 111};
+    write(fd, in.data(), in.size());
+    close(fd);
+    length = in.size() + 1000;
+    rc = handler.readFile(tmplt, 0, length, response);
+    ASSERT_EQ(rc, PLDM_SUCCESS);
+    ASSERT_EQ(length, in.size());
+    ASSERT_EQ(response.size(), in.size());
+    ASSERT_EQ(std::equal(in.begin(), in.end(), response.begin()), true);
+}