oem-ibm responder : Implement ReadFile and WriteFile
ReadFile and WriteFile command is required for in-band
file transfers via LPC channel. These commands will be
mostly used for files that are small size.
This commit covers responder for ReadFile and WriteFile.
Change-Id: Id2cbf7afb9aa3ed193376bc93eaae5b8a334d5f7
Signed-off-by: vkaverap <vkaverap@in.ibm.com>
diff --git a/oem/ibm/libpldmresponder/file_io.cpp b/oem/ibm/libpldmresponder/file_io.cpp
index a3a81b4..24997fb 100644
--- a/oem/ibm/libpldmresponder/file_io.cpp
+++ b/oem/ibm/libpldmresponder/file_io.cpp
@@ -34,6 +34,8 @@
std::move(readFileIntoMemory));
registerHandler(PLDM_OEM, PLDM_WRITE_FILE_FROM_MEMORY,
std::move(writeFileFromMemory));
+ registerHandler(PLDM_OEM, PLDM_READ_FILE, std::move(readFile));
+ registerHandler(PLDM_OEM, PLDM_WRITE_FILE, std::move(writeFile));
}
} // namespace oem_ibm
@@ -366,5 +368,160 @@
return response;
}
+Response readFile(const pldm_msg* request, size_t payloadLength)
+{
+ uint32_t fileHandle = 0;
+ uint32_t offset = 0;
+ uint32_t length = 0;
+
+ Response response(sizeof(pldm_msg_hdr) + PLDM_READ_FILE_RESP_BYTES);
+ auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
+
+ if (payloadLength != PLDM_READ_FILE_REQ_BYTES)
+ {
+ encode_read_file_resp(request->hdr.instance_id,
+ PLDM_ERROR_INVALID_LENGTH, length, responsePtr);
+ return response;
+ }
+
+ auto rc = decode_read_file_req(request, payloadLength, &fileHandle, &offset,
+ &length);
+
+ if (rc)
+ {
+ encode_read_file_resp(request->hdr.instance_id, rc, 0, responsePtr);
+ return response;
+ }
+
+ 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_read_file_resp(request->hdr.instance_id,
+ PLDM_INVALID_FILE_HANDLE, length, responsePtr);
+ return response;
+ }
+
+ if (!fs::exists(value.fsPath))
+ {
+ log<level::ERR>("File does not exist", entry("HANDLE=%d", fileHandle));
+ encode_read_file_resp(request->hdr.instance_id,
+ PLDM_INVALID_FILE_HANDLE, length, responsePtr);
+ return response;
+ }
+
+ auto fileSize = fs::file_size(value.fsPath);
+ if (offset >= fileSize)
+ {
+ log<level::ERR>("Offset exceeds file size", entry("OFFSET=%d", offset),
+ entry("FILE_SIZE=%d", fileSize));
+ encode_read_file_resp(request->hdr.instance_id, PLDM_DATA_OUT_OF_RANGE,
+ length, responsePtr);
+ return response;
+ }
+
+ if (offset + length > fileSize)
+ {
+ length = fileSize - offset;
+ }
+
+ response.resize(response.size() + length);
+ responsePtr = reinterpret_cast<pldm_msg*>(response.data());
+ auto fileDataPos = reinterpret_cast<char*>(responsePtr);
+ fileDataPos += sizeof(pldm_msg_hdr) + sizeof(uint8_t) + sizeof(length);
+
+ std::ifstream stream(value.fsPath, std::ios::in | std::ios::binary);
+ stream.seekg(offset);
+ stream.read(fileDataPos, length);
+
+ encode_read_file_resp(request->hdr.instance_id, PLDM_SUCCESS, length,
+ responsePtr);
+
+ return response;
+}
+
+Response writeFile(const pldm_msg* request, size_t payloadLength)
+{
+ uint32_t fileHandle = 0;
+ uint32_t offset = 0;
+ uint32_t length = 0;
+ size_t fileDataOffset = 0;
+
+ Response response(sizeof(pldm_msg_hdr) + PLDM_WRITE_FILE_RESP_BYTES);
+ auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
+
+ if (payloadLength < PLDM_WRITE_FILE_REQ_BYTES)
+ {
+ encode_write_file_resp(request->hdr.instance_id,
+ PLDM_ERROR_INVALID_LENGTH, 0, responsePtr);
+ return response;
+ }
+
+ auto rc = decode_write_file_req(request, payloadLength, &fileHandle,
+ &offset, &length, &fileDataOffset);
+
+ if (rc)
+ {
+ encode_write_file_resp(request->hdr.instance_id, rc, 0, responsePtr);
+ return response;
+ }
+
+ 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_write_file_resp(request->hdr.instance_id,
+ 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_write_file_resp(request->hdr.instance_id,
+ PLDM_INVALID_FILE_HANDLE, 0, responsePtr);
+ return response;
+ }
+
+ auto fileSize = fs::file_size(value.fsPath);
+ if (offset >= fileSize)
+ {
+ log<level::ERR>("Offset exceeds file size", entry("OFFSET=%d", offset),
+ entry("FILE_SIZE=%d", fileSize));
+ encode_write_file_resp(request->hdr.instance_id, PLDM_DATA_OUT_OF_RANGE,
+ 0, responsePtr);
+ return response;
+ }
+
+ auto fileDataPos =
+ reinterpret_cast<const char*>(request->payload) + fileDataOffset;
+
+ std::ofstream stream(value.fsPath,
+ std::ios::in | std::ios::out | std::ios::binary);
+ stream.seekp(offset);
+ stream.write(fileDataPos, length);
+
+ encode_write_file_resp(request->hdr.instance_id, PLDM_SUCCESS, 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 30236ce..0ee4ed6 100644
--- a/oem/ibm/libpldmresponder/file_io.hpp
+++ b/oem/ibm/libpldmresponder/file_io.hpp
@@ -147,5 +147,23 @@
* @return PLDM response message
*/
Response getFileTable(const pldm_msg* request, size_t payloadLength);
+
+/** @brief Handler for readFile command
+ *
+ * @param[in] request - PLDM request msg
+ * @param[in] payloadLength - length of the message payload
+ *
+ * @return PLDM response message
+ */
+Response readFile(const pldm_msg* request, size_t payloadLength);
+
+/** @brief Handler for writeFile command
+ *
+ * @param[in] request - PLDM request msg
+ * @param[in] payloadLength - length of the message payload
+ *
+ * @return PLDM response message
+ */
+Response writeFile(const pldm_msg* request, size_t payloadLength);
} // namespace responder
} // namespace pldm
diff --git a/oem/ibm/test/libpldmresponder_fileio_test.cpp b/oem/ibm/test/libpldmresponder_fileio_test.cpp
index d44854f..c132895 100644
--- a/oem/ibm/test/libpldmresponder_fileio_test.cpp
+++ b/oem/ibm/test/libpldmresponder_fileio_test.cpp
@@ -560,3 +560,185 @@
auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
ASSERT_EQ(responsePtr->payload[0], PLDM_INVALID_FILE_TABLE_TYPE);
}
+
+TEST_F(TestFileTable, ReadFileBadPath)
+{
+ uint32_t fileHandle = 1;
+ uint32_t offset = 0;
+ uint32_t length = 0x4;
+
+ std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_READ_FILE_REQ_BYTES>
+ requestMsg{};
+ auto requestMsgPtr = reinterpret_cast<pldm_msg*>(requestMsg.data());
+ auto payload_length = requestMsg.size() - sizeof(pldm_msg_hdr);
+ auto request = reinterpret_cast<pldm_read_file_req*>(requestMsg.data() +
+ sizeof(pldm_msg_hdr));
+
+ request->file_handle = fileHandle;
+ request->offset = offset;
+ request->length = length;
+
+ using namespace pldm::filetable;
+ // Initialise the file table with 2 valid file handles 0 & 1.
+ auto& table = buildFileTable(fileTableConfig.c_str());
+
+ // Invalid payload length
+ auto response = readFile(requestMsgPtr, 0);
+ auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
+ ASSERT_EQ(responsePtr->payload[0], PLDM_ERROR_INVALID_LENGTH);
+
+ // Data out of range. File size is 1024, offset = 1024 is invalid.
+ request->offset = 1024;
+
+ response = readFile(requestMsgPtr, payload_length);
+ responsePtr = reinterpret_cast<pldm_msg*>(response.data());
+ ASSERT_EQ(responsePtr->payload[0], PLDM_DATA_OUT_OF_RANGE);
+
+ // Invalid file handle
+ request->file_handle = 2;
+
+ response = readFile(requestMsgPtr, payload_length);
+ responsePtr = reinterpret_cast<pldm_msg*>(response.data());
+ ASSERT_EQ(responsePtr->payload[0], PLDM_INVALID_FILE_HANDLE);
+
+ table.clear();
+}
+
+TEST_F(TestFileTable, ReadFileGoodPath)
+{
+ uint32_t fileHandle = 0;
+ uint32_t offset = 0;
+ uint32_t length = 0x4;
+
+ std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_READ_FILE_REQ_BYTES>
+ requestMsg{};
+ auto requestMsgPtr = reinterpret_cast<pldm_msg*>(requestMsg.data());
+ auto payload_length = requestMsg.size() - sizeof(pldm_msg_hdr);
+ auto request = reinterpret_cast<pldm_read_file_req*>(requestMsg.data() +
+ sizeof(pldm_msg_hdr));
+
+ request->file_handle = fileHandle;
+ request->offset = offset;
+ request->length = length;
+
+ using namespace pldm::filetable;
+ // Initialise the file table with 2 valid file handles 0 & 1.
+ auto& table = buildFileTable(fileTableConfig.c_str());
+ FileEntry value{};
+ value = table.at(fileHandle);
+
+ std::ifstream stream(value.fsPath, std::ios::in | std::ios::binary);
+ stream.seekg(offset);
+ std::vector<char> buffer(length);
+ stream.read(buffer.data(), length);
+
+ auto responseMsg = readFile(requestMsgPtr, payload_length);
+ auto response = reinterpret_cast<pldm_read_file_resp*>(
+ responseMsg.data() + sizeof(pldm_msg_hdr));
+ ASSERT_EQ(response->completion_code, PLDM_SUCCESS);
+ ASSERT_EQ(response->length, length);
+ ASSERT_EQ(0, memcmp(response->file_data, buffer.data(), length));
+
+ // Test condition offset + length > fileSize;
+ size_t fileSize = 1024;
+ request->offset = 1023;
+ request->length = 10;
+
+ stream.seekg(request->offset);
+ buffer.resize(fileSize - request->offset);
+ stream.read(buffer.data(), (fileSize - request->offset));
+
+ responseMsg = readFile(requestMsgPtr, payload_length);
+ response = reinterpret_cast<pldm_read_file_resp*>(responseMsg.data() +
+ sizeof(pldm_msg_hdr));
+ ASSERT_EQ(response->completion_code, PLDM_SUCCESS);
+ ASSERT_EQ(response->length, (fileSize - request->offset));
+ ASSERT_EQ(0, memcmp(response->file_data, buffer.data(),
+ (fileSize - request->offset)));
+
+ table.clear();
+}
+
+TEST_F(TestFileTable, WriteFileBadPath)
+{
+ uint32_t fileHandle = 0;
+ uint32_t offset = 0;
+ uint32_t length = 0x10;
+
+ std::vector<uint8_t> requestMsg(sizeof(pldm_msg_hdr) +
+ PLDM_WRITE_FILE_REQ_BYTES + length);
+ auto requestMsgPtr = reinterpret_cast<pldm_msg*>(requestMsg.data());
+ auto payload_length = requestMsg.size() - sizeof(pldm_msg_hdr);
+ auto request = reinterpret_cast<pldm_write_file_req*>(requestMsg.data() +
+ sizeof(pldm_msg_hdr));
+
+ using namespace pldm::filetable;
+ // Initialise the file table with 2 valid file handles 0 & 1.
+ auto& table = buildFileTable(fileTableConfig.c_str());
+
+ request->file_handle = fileHandle;
+ request->offset = offset;
+ request->length = length;
+
+ // Invalid payload length
+ auto response = writeFile(requestMsgPtr, 0);
+ auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
+ ASSERT_EQ(responsePtr->payload[0], PLDM_ERROR_INVALID_LENGTH);
+
+ // Data out of range. File size is 1024, offset = 1024 is invalid.
+ request->offset = 1024;
+
+ response = writeFile(requestMsgPtr, payload_length);
+ responsePtr = reinterpret_cast<pldm_msg*>(response.data());
+ ASSERT_EQ(responsePtr->payload[0], PLDM_DATA_OUT_OF_RANGE);
+
+ // Invalid file handle
+ request->file_handle = 2;
+
+ response = writeFile(requestMsgPtr, payload_length);
+ responsePtr = reinterpret_cast<pldm_msg*>(response.data());
+ ASSERT_EQ(responsePtr->payload[0], PLDM_INVALID_FILE_HANDLE);
+
+ table.clear();
+}
+
+TEST_F(TestFileTable, WriteFileGoodPath)
+{
+ uint32_t fileHandle = 1;
+ uint32_t offset = 0;
+ std::array<uint8_t, 4> fileData = {0x41, 0x42, 0x43, 0x44};
+ uint32_t length = fileData.size();
+
+ std::vector<uint8_t> requestMsg(sizeof(pldm_msg_hdr) +
+ PLDM_WRITE_FILE_REQ_BYTES + length);
+ auto requestMsgPtr = reinterpret_cast<pldm_msg*>(requestMsg.data());
+ auto payload_length = requestMsg.size() - sizeof(pldm_msg_hdr);
+ auto request = reinterpret_cast<pldm_write_file_req*>(requestMsg.data() +
+ sizeof(pldm_msg_hdr));
+
+ using namespace pldm::filetable;
+ // Initialise the file table with 2 valid file handles 0 & 1.
+ auto& table = buildFileTable(fileTableConfig.c_str());
+ FileEntry value{};
+ value = table.at(fileHandle);
+
+ request->file_handle = fileHandle;
+ request->offset = offset;
+ request->length = length;
+ memcpy(request->file_data, fileData.data(), fileData.size());
+
+ auto responseMsg = writeFile(requestMsgPtr, payload_length);
+ auto response = reinterpret_cast<pldm_read_file_resp*>(
+ responseMsg.data() + sizeof(pldm_msg_hdr));
+
+ std::ifstream stream(value.fsPath, std::ios::in | std::ios::binary);
+ stream.seekg(offset);
+ std::vector<char> buffer(length);
+ stream.read(buffer.data(), length);
+
+ ASSERT_EQ(response->completion_code, PLDM_SUCCESS);
+ ASSERT_EQ(response->length, length);
+ ASSERT_EQ(0, memcmp(fileData.data(), buffer.data(), length));
+
+ table.clear();
+}