1 #include "libpldmresponder/file_io.hpp"
2 #include "libpldmresponder/file_io_by_type.hpp"
3 #include "libpldmresponder/file_io_type_lid.hpp"
4 #include "libpldmresponder/file_io_type_pel.hpp"
5 #include "libpldmresponder/file_table.hpp"
6 #include "xyz/openbmc_project/Common/error.hpp"
7 
8 #include <filesystem>
9 #include <fstream>
10 #include <nlohmann/json.hpp>
11 #include <phosphor-logging/elog-errors.hpp>
12 
13 #include "libpldm/base.h"
14 #include "libpldm/file_io.h"
15 
16 #include <gmock/gmock-matchers.h>
17 #include <gmock/gmock.h>
18 #include <gtest/gtest.h>
19 
20 namespace fs = std::filesystem;
21 using Json = nlohmann::json;
22 using namespace pldm::filetable;
23 using namespace pldm::responder;
24 
25 class TestFileTable : public testing::Test
26 {
27   public:
28     void SetUp() override
29     {
30         // Create a temporary directory to hold the config file and files to
31         // populate the file table.
32         char tmppldm[] = "/tmp/pldm_fileio_table.XXXXXX";
33         dir = fs::path(mkdtemp(tmppldm));
34 
35         // Copy the sample image files to the directory
36         fs::copy("./files", dir);
37 
38         imageFile = dir / "NVRAM-IMAGE";
39         auto jsonObjects = Json::array();
40         auto obj = Json::object();
41         obj["path"] = imageFile.c_str();
42         obj["file_traits"] = 1;
43 
44         jsonObjects.push_back(obj);
45         obj.clear();
46         cksumFile = dir / "NVRAM-IMAGE-CKSUM";
47         obj["path"] = cksumFile.c_str();
48         obj["file_traits"] = 4;
49         jsonObjects.push_back(obj);
50 
51         fileTableConfig = dir / "configFile.json";
52         std::ofstream file(fileTableConfig.c_str());
53         file << std::setw(4) << jsonObjects << std::endl;
54     }
55 
56     void TearDown() override
57     {
58         fs::remove_all(dir);
59     }
60 
61     fs::path dir;
62     fs::path imageFile;
63     fs::path cksumFile;
64     fs::path fileTableConfig;
65 
66     // <4 bytes - File handle - 0 (0x00 0x00 0x00 0x00)>,
67     // <2 bytes - Filename length - 11 (0x0b 0x00>
68     // <11 bytes - Filename - ASCII for NVRAM-IMAGE>
69     // <4 bytes - File size - 1024 (0x00 0x04 0x00 0x00)>
70     // <4 bytes - File traits - 1 (0x01 0x00 0x00 0x00)>
71     // <4 bytes - File handle - 1 (0x01 0x00 0x00 0x00)>,
72     // <2 bytes - Filename length - 17 (0x11 0x00>
73     // <17 bytes - Filename - ASCII for NVRAM-IMAGE-CKSUM>
74     // <4 bytes - File size - 16 (0x0f 0x00 0x00 0x00)>
75     // <4 bytes - File traits - 4 (0x04 0x00 0x00 0x00)>
76     // No pad bytes added since the length for both the file entries in the
77     // table is 56, which is a multiple of 4.
78     // <4 bytes - Checksum - 2088303182(0x4e 0xfa 0x78 0x7c)>
79     Table attrTable = {
80         0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x4e, 0x56, 0x52, 0x41, 0x4d, 0x2d,
81         0x49, 0x4d, 0x41, 0x47, 0x45, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00,
82         0x00, 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x4e, 0x56, 0x52, 0x41, 0x4d,
83         0x2d, 0x49, 0x4d, 0x41, 0x47, 0x45, 0x2d, 0x43, 0x4b, 0x53, 0x55, 0x4d,
84         0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x4e, 0xfa, 0x78, 0x7c};
85 };
86 
87 namespace pldm
88 {
89 
90 namespace responder
91 {
92 
93 namespace dma
94 {
95 
96 class MockDMA
97 {
98   public:
99     MOCK_METHOD5(transferDataHost,
100                  int(const fs::path& file, uint32_t offset, uint32_t length,
101                      uint64_t address, bool upstream));
102 };
103 
104 } // namespace dma
105 } // namespace responder
106 } // namespace pldm
107 using namespace pldm::responder;
108 using ::testing::_;
109 using ::testing::Return;
110 
111 TEST(TransferDataHost, GoodPath)
112 {
113     using namespace pldm::responder::dma;
114 
115     MockDMA dmaObj;
116     fs::path path("");
117 
118     // Minimum length of 16 and expect transferDataHost to be called once
119     // returns the default value of 0 (the return type of transferDataHost is
120     // int, the default value for int is 0)
121     uint32_t length = minSize;
122     EXPECT_CALL(dmaObj, transferDataHost(path, 0, length, 0, true)).Times(1);
123     auto response = transferAll<MockDMA>(&dmaObj, PLDM_READ_FILE_INTO_MEMORY,
124                                          path, 0, length, 0, true, 0);
125     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
126     ASSERT_EQ(responsePtr->payload[0], PLDM_SUCCESS);
127     ASSERT_EQ(0, memcmp(responsePtr->payload + sizeof(responsePtr->payload[0]),
128                         &length, sizeof(length)));
129 
130     // maxsize of DMA
131     length = maxSize;
132     EXPECT_CALL(dmaObj, transferDataHost(path, 0, length, 0, true)).Times(1);
133     response = transferAll<MockDMA>(&dmaObj, PLDM_READ_FILE_INTO_MEMORY, path,
134                                     0, length, 0, true, 0);
135     responsePtr = reinterpret_cast<pldm_msg*>(response.data());
136     ASSERT_EQ(responsePtr->payload[0], PLDM_SUCCESS);
137     ASSERT_EQ(0, memcmp(responsePtr->payload + sizeof(responsePtr->payload[0]),
138                         &length, sizeof(length)));
139 
140     // length greater than maxsize of DMA
141     length = maxSize + minSize;
142     EXPECT_CALL(dmaObj, transferDataHost(path, 0, maxSize, 0, true)).Times(1);
143     EXPECT_CALL(dmaObj, transferDataHost(path, maxSize, minSize, maxSize, true))
144         .Times(1);
145     response = transferAll<MockDMA>(&dmaObj, PLDM_READ_FILE_INTO_MEMORY, path,
146                                     0, length, 0, true, 0);
147     responsePtr = reinterpret_cast<pldm_msg*>(response.data());
148     ASSERT_EQ(responsePtr->payload[0], PLDM_SUCCESS);
149     ASSERT_EQ(0, memcmp(responsePtr->payload + sizeof(responsePtr->payload[0]),
150                         &length, sizeof(length)));
151 
152     // length greater than 2*maxsize of DMA
153     length = 3 * maxSize;
154     EXPECT_CALL(dmaObj, transferDataHost(_, _, _, _, true)).Times(3);
155     response = transferAll<MockDMA>(&dmaObj, PLDM_READ_FILE_INTO_MEMORY, path,
156                                     0, length, 0, true, 0);
157     responsePtr = reinterpret_cast<pldm_msg*>(response.data());
158     ASSERT_EQ(responsePtr->payload[0], PLDM_SUCCESS);
159     ASSERT_EQ(0, memcmp(responsePtr->payload + sizeof(responsePtr->payload[0]),
160                         &length, sizeof(length)));
161 
162     // check for downstream(copy data from host to BMC) parameter
163     length = minSize;
164     EXPECT_CALL(dmaObj, transferDataHost(path, 0, length, 0, false)).Times(1);
165     response = transferAll<MockDMA>(&dmaObj, PLDM_READ_FILE_INTO_MEMORY, path,
166                                     0, length, 0, false, 0);
167     responsePtr = reinterpret_cast<pldm_msg*>(response.data());
168     ASSERT_EQ(responsePtr->payload[0], PLDM_SUCCESS);
169     ASSERT_EQ(0, memcmp(responsePtr->payload + sizeof(responsePtr->payload[0]),
170                         &length, sizeof(length)));
171 }
172 
173 TEST(TransferDataHost, BadPath)
174 {
175     using namespace pldm::responder::dma;
176 
177     MockDMA dmaObj;
178     fs::path path("");
179 
180     // Minimum length of 16 and transferDataHost returning a negative errno
181     uint32_t length = minSize;
182     EXPECT_CALL(dmaObj, transferDataHost(_, _, _, _, _)).WillOnce(Return(-1));
183     auto response = transferAll<MockDMA>(&dmaObj, PLDM_READ_FILE_INTO_MEMORY,
184                                          path, 0, length, 0, true, 0);
185     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
186     ASSERT_EQ(responsePtr->payload[0], PLDM_ERROR);
187 
188     // length greater than maxsize of DMA and transferDataHost returning a
189     // negative errno
190     length = maxSize + minSize;
191     EXPECT_CALL(dmaObj, transferDataHost(_, _, _, _, _)).WillOnce(Return(-1));
192     response = transferAll<MockDMA>(&dmaObj, PLDM_READ_FILE_INTO_MEMORY, path,
193                                     0, length, 0, true, 0);
194     responsePtr = reinterpret_cast<pldm_msg*>(response.data());
195     ASSERT_EQ(responsePtr->payload[0], PLDM_ERROR);
196 }
197 
198 TEST(ReadFileIntoMemory, BadPath)
199 {
200     uint32_t fileHandle = 0;
201     uint32_t offset = 0;
202     uint32_t length = 10;
203     uint64_t address = 0;
204 
205     std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_RW_FILE_MEM_REQ_BYTES>
206         requestMsg{};
207     auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
208     memcpy(request->payload, &fileHandle, sizeof(fileHandle));
209     memcpy(request->payload + sizeof(fileHandle), &offset, sizeof(offset));
210     memcpy(request->payload + sizeof(fileHandle) + sizeof(offset), &length,
211            sizeof(length));
212     memcpy(request->payload + sizeof(fileHandle) + sizeof(offset) +
213                sizeof(length),
214            &address, sizeof(address));
215 
216     // Pass invalid payload length
217     oem_ibm::Handler handler;
218     auto response = handler.readFileIntoMemory(request, 0);
219     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
220     ASSERT_EQ(responsePtr->payload[0], PLDM_ERROR_INVALID_LENGTH);
221 }
222 
223 TEST_F(TestFileTable, ReadFileInvalidFileHandle)
224 {
225     // Invalid file handle in the file table
226     uint32_t fileHandle = 2;
227     uint32_t offset = 0;
228     uint32_t length = 0;
229     uint64_t address = 0;
230 
231     std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_RW_FILE_MEM_REQ_BYTES>
232         requestMsg{};
233     auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
234     size_t requestPayloadLength = requestMsg.size() - sizeof(pldm_msg_hdr);
235     memcpy(request->payload, &fileHandle, sizeof(fileHandle));
236     memcpy(request->payload + sizeof(fileHandle), &offset, sizeof(offset));
237     memcpy(request->payload + sizeof(fileHandle) + sizeof(offset), &length,
238            sizeof(length));
239     memcpy(request->payload + sizeof(fileHandle) + sizeof(offset) +
240                sizeof(length),
241            &address, sizeof(address));
242 
243     using namespace pldm::filetable;
244     // Initialise the file table with 2 valid file handles 0 & 1.
245     auto& table = buildFileTable(fileTableConfig.c_str());
246 
247     oem_ibm::Handler handler;
248     auto response = handler.readFileIntoMemory(request, requestPayloadLength);
249     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
250     ASSERT_EQ(responsePtr->payload[0], PLDM_INVALID_FILE_HANDLE);
251     // Clear the file table contents.
252     table.clear();
253 }
254 
255 TEST_F(TestFileTable, ReadFileInvalidOffset)
256 {
257     uint32_t fileHandle = 0;
258     // The file size is 1024, so the offset is invalid
259     uint32_t offset = 1024;
260     uint32_t length = 0;
261     uint64_t address = 0;
262 
263     std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_RW_FILE_MEM_REQ_BYTES>
264         requestMsg{};
265     auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
266     size_t requestPayloadLength = requestMsg.size() - sizeof(pldm_msg_hdr);
267     memcpy(request->payload, &fileHandle, sizeof(fileHandle));
268     memcpy(request->payload + sizeof(fileHandle), &offset, sizeof(offset));
269     memcpy(request->payload + sizeof(fileHandle) + sizeof(offset), &length,
270            sizeof(length));
271     memcpy(request->payload + sizeof(fileHandle) + sizeof(offset) +
272                sizeof(length),
273            &address, sizeof(address));
274 
275     using namespace pldm::filetable;
276     auto& table = buildFileTable(fileTableConfig.c_str());
277 
278     oem_ibm::Handler handler;
279     auto response = handler.readFileIntoMemory(request, requestPayloadLength);
280     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
281     ASSERT_EQ(responsePtr->payload[0], PLDM_DATA_OUT_OF_RANGE);
282     // Clear the file table contents.
283     table.clear();
284 }
285 
286 TEST_F(TestFileTable, ReadFileInvalidLength)
287 {
288     uint32_t fileHandle = 0;
289     uint32_t offset = 100;
290     // Length should be a multiple of dma min size(16)
291     uint32_t length = 10;
292     uint64_t address = 0;
293 
294     std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_RW_FILE_MEM_REQ_BYTES>
295         requestMsg{};
296     auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
297     size_t requestPayloadLength = requestMsg.size() - sizeof(pldm_msg_hdr);
298     memcpy(request->payload, &fileHandle, sizeof(fileHandle));
299     memcpy(request->payload + sizeof(fileHandle), &offset, sizeof(offset));
300     memcpy(request->payload + sizeof(fileHandle) + sizeof(offset), &length,
301            sizeof(length));
302     memcpy(request->payload + sizeof(fileHandle) + sizeof(offset) +
303                sizeof(length),
304            &address, sizeof(address));
305 
306     using namespace pldm::filetable;
307     auto& table = buildFileTable(fileTableConfig.c_str());
308 
309     oem_ibm::Handler handler;
310     auto response = handler.readFileIntoMemory(request, requestPayloadLength);
311     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
312     ASSERT_EQ(responsePtr->payload[0], PLDM_INVALID_READ_LENGTH);
313     // Clear the file table contents.
314     table.clear();
315 }
316 
317 TEST_F(TestFileTable, ReadFileInvalidEffectiveLength)
318 {
319     uint32_t fileHandle = 0;
320     // valid offset
321     uint32_t offset = 100;
322     // length + offset exceeds the size, so effective length is
323     // filesize(1024) - offset(100). The effective length is not a multiple of
324     // DMA min size(16)
325     uint32_t length = 1024;
326     uint64_t address = 0;
327 
328     std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_RW_FILE_MEM_REQ_BYTES>
329         requestMsg{};
330     auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
331     size_t requestPayloadLength = requestMsg.size() - sizeof(pldm_msg_hdr);
332     memcpy(request->payload, &fileHandle, sizeof(fileHandle));
333     memcpy(request->payload + sizeof(fileHandle), &offset, sizeof(offset));
334     memcpy(request->payload + sizeof(fileHandle) + sizeof(offset), &length,
335            sizeof(length));
336     memcpy(request->payload + sizeof(fileHandle) + sizeof(offset) +
337                sizeof(length),
338            &address, sizeof(address));
339 
340     using namespace pldm::filetable;
341     auto& table = buildFileTable(fileTableConfig.c_str());
342 
343     oem_ibm::Handler handler;
344     auto response = handler.readFileIntoMemory(request, requestPayloadLength);
345     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
346     ASSERT_EQ(responsePtr->payload[0], PLDM_INVALID_READ_LENGTH);
347     // Clear the file table contents.
348     table.clear();
349 }
350 
351 TEST(WriteFileFromMemory, BadPath)
352 {
353     uint32_t fileHandle = 0;
354     uint32_t offset = 0;
355     uint32_t length = 10;
356     uint64_t address = 0;
357 
358     std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_RW_FILE_MEM_REQ_BYTES>
359         requestMsg{};
360     auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
361     size_t requestPayloadLength = requestMsg.size() - sizeof(pldm_msg_hdr);
362     memcpy(request->payload, &fileHandle, sizeof(fileHandle));
363     memcpy(request->payload + sizeof(fileHandle), &offset, sizeof(offset));
364     memcpy(request->payload + sizeof(fileHandle) + sizeof(offset), &length,
365            sizeof(length));
366     memcpy(request->payload + sizeof(fileHandle) + sizeof(offset) +
367                sizeof(length),
368            &address, sizeof(address));
369 
370     // Pass invalid payload length
371     oem_ibm::Handler handler;
372     auto response = handler.writeFileFromMemory(request, 0);
373     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
374     ASSERT_EQ(responsePtr->payload[0], PLDM_ERROR_INVALID_LENGTH);
375 
376     // The length field is not a multiple of DMA minsize
377     response = handler.writeFileFromMemory(request, requestPayloadLength);
378     responsePtr = reinterpret_cast<pldm_msg*>(response.data());
379     ASSERT_EQ(responsePtr->payload[0], PLDM_INVALID_WRITE_LENGTH);
380 }
381 
382 TEST_F(TestFileTable, WriteFileInvalidFileHandle)
383 {
384     // Invalid file handle in the file table
385     uint32_t fileHandle = 2;
386     uint32_t offset = 0;
387     uint32_t length = 16;
388     uint64_t address = 0;
389 
390     std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_RW_FILE_MEM_REQ_BYTES>
391         requestMsg{};
392     auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
393     size_t requestPayloadLength = requestMsg.size() - sizeof(pldm_msg_hdr);
394     memcpy(request->payload, &fileHandle, sizeof(fileHandle));
395     memcpy(request->payload + sizeof(fileHandle), &offset, sizeof(offset));
396     memcpy(request->payload + sizeof(fileHandle) + sizeof(offset), &length,
397            sizeof(length));
398     memcpy(request->payload + sizeof(fileHandle) + sizeof(offset) +
399                sizeof(length),
400            &address, sizeof(address));
401 
402     using namespace pldm::filetable;
403     // Initialise the file table with 2 valid file handles 0 & 1.
404     auto& table = buildFileTable(fileTableConfig.c_str());
405 
406     oem_ibm::Handler handler;
407     auto response = handler.writeFileFromMemory(request, requestPayloadLength);
408     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
409     ASSERT_EQ(responsePtr->payload[0], PLDM_INVALID_FILE_HANDLE);
410     // Clear the file table contents.
411     table.clear();
412 }
413 
414 TEST_F(TestFileTable, WriteFileInvalidOffset)
415 {
416     uint32_t fileHandle = 0;
417     // The file size is 1024, so the offset is invalid
418     uint32_t offset = 1024;
419     uint32_t length = 16;
420     uint64_t address = 0;
421 
422     std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_RW_FILE_MEM_REQ_BYTES>
423         requestMsg{};
424     auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
425     size_t requestPayloadLength = requestMsg.size() - sizeof(pldm_msg_hdr);
426     memcpy(request->payload, &fileHandle, sizeof(fileHandle));
427     memcpy(request->payload + sizeof(fileHandle), &offset, sizeof(offset));
428     memcpy(request->payload + sizeof(fileHandle) + sizeof(offset), &length,
429            sizeof(length));
430     memcpy(request->payload + sizeof(fileHandle) + sizeof(offset) +
431                sizeof(length),
432            &address, sizeof(address));
433 
434     using namespace pldm::filetable;
435     // Initialise the file table with 2 valid file handles 0 & 1.
436     auto& table = buildFileTable(TestFileTable::fileTableConfig.c_str());
437 
438     oem_ibm::Handler handler;
439     auto response = handler.writeFileFromMemory(request, requestPayloadLength);
440     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
441     ASSERT_EQ(responsePtr->payload[0], PLDM_DATA_OUT_OF_RANGE);
442     // Clear the file table contents.
443     table.clear();
444 }
445 
446 TEST(FileTable, ConfigNotExist)
447 {
448     FileTable tableObj("");
449     EXPECT_EQ(tableObj.isEmpty(), true);
450 }
451 
452 TEST_F(TestFileTable, ValidateFileEntry)
453 {
454     FileTable tableObj(fileTableConfig.c_str());
455 
456     // Test file handle 0, the file size is 1K bytes.
457     auto value = tableObj.at(0);
458     ASSERT_EQ(value.handle, 0);
459     ASSERT_EQ(strcmp(value.fsPath.c_str(), imageFile.c_str()), 0);
460     ASSERT_EQ(static_cast<uint32_t>(fs::file_size(value.fsPath)), 1024);
461     ASSERT_EQ(value.traits.value, 1);
462     ASSERT_EQ(true, fs::exists(value.fsPath));
463 
464     // Test file handle 1, the file size is 16 bytes
465     auto value1 = tableObj.at(1);
466     ASSERT_EQ(value1.handle, 1);
467     ASSERT_EQ(strcmp(value1.fsPath.c_str(), cksumFile.c_str()), 0);
468     ASSERT_EQ(static_cast<uint32_t>(fs::file_size(value1.fsPath)), 16);
469     ASSERT_EQ(value1.traits.value, 4);
470     ASSERT_EQ(true, fs::exists(value1.fsPath));
471 
472     // Test invalid file handle
473     ASSERT_THROW(tableObj.at(2), std::out_of_range);
474 }
475 
476 TEST_F(TestFileTable, ValidateFileTable)
477 {
478     FileTable tableObj(fileTableConfig.c_str());
479 
480     // Validate file attribute table
481     auto table = tableObj();
482     ASSERT_EQ(true,
483               std::equal(attrTable.begin(), attrTable.end(), table.begin()));
484 }
485 
486 TEST_F(TestFileTable, GetFileTableCommand)
487 {
488     // Initialise the file table with a valid handle of 0 & 1
489     auto& table = buildFileTable(fileTableConfig.c_str());
490 
491     uint32_t transferHandle = 0;
492     uint8_t opFlag = 0;
493     uint8_t type = PLDM_FILE_ATTRIBUTE_TABLE;
494     uint32_t nextTransferHandle = 0;
495     uint8_t transferFlag = PLDM_START_AND_END;
496 
497     std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_GET_FILE_TABLE_REQ_BYTES>
498         requestMsg{};
499     auto requestMsgPtr = reinterpret_cast<pldm_msg*>(requestMsg.data());
500     size_t requestPayloadLength = requestMsg.size() - sizeof(pldm_msg_hdr);
501     auto request = reinterpret_cast<pldm_get_file_table_req*>(
502         requestMsg.data() + sizeof(pldm_msg_hdr));
503     request->transfer_handle = transferHandle;
504     request->operation_flag = opFlag;
505     request->table_type = type;
506 
507     oem_ibm::Handler handler;
508     auto response = handler.getFileTable(requestMsgPtr, requestPayloadLength);
509     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
510     ASSERT_EQ(responsePtr->payload[0], PLDM_SUCCESS);
511     size_t offsetSize = sizeof(responsePtr->payload[0]);
512     ASSERT_EQ(0, memcmp(responsePtr->payload + offsetSize, &nextTransferHandle,
513                         sizeof(nextTransferHandle)));
514     offsetSize += sizeof(nextTransferHandle);
515     ASSERT_EQ(0, memcmp(responsePtr->payload + offsetSize, &transferFlag,
516                         sizeof(transferFlag)));
517     offsetSize += sizeof(transferFlag);
518     ASSERT_EQ(0, memcmp(responsePtr->payload + offsetSize, attrTable.data(),
519                         attrTable.size()));
520     table.clear();
521 }
522 
523 TEST_F(TestFileTable, GetFileTableCommandReqLengthMismatch)
524 {
525     std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_GET_FILE_TABLE_REQ_BYTES>
526         requestMsg{};
527     auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
528 
529     // Pass invalid command payload length
530     oem_ibm::Handler handler;
531     auto response = handler.getFileTable(request, 0);
532     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
533     ASSERT_EQ(responsePtr->payload[0], PLDM_ERROR_INVALID_LENGTH);
534 }
535 
536 TEST_F(TestFileTable, GetFileTableCommandOEMAttrTable)
537 {
538     uint32_t transferHandle = 0;
539     uint8_t opFlag = 0;
540     uint8_t type = PLDM_OEM_FILE_ATTRIBUTE_TABLE;
541 
542     std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_GET_FILE_TABLE_REQ_BYTES>
543         requestMsg{};
544     auto requestMsgPtr = reinterpret_cast<pldm_msg*>(requestMsg.data());
545     size_t requestPayloadLength = requestMsg.size() - sizeof(pldm_msg_hdr);
546     auto request = reinterpret_cast<pldm_get_file_table_req*>(
547         requestMsg.data() + sizeof(pldm_msg_hdr));
548     request->transfer_handle = transferHandle;
549     request->operation_flag = opFlag;
550     request->table_type = type;
551 
552     oem_ibm::Handler handler;
553     auto response = handler.getFileTable(requestMsgPtr, requestPayloadLength);
554     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
555     ASSERT_EQ(responsePtr->payload[0], PLDM_INVALID_FILE_TABLE_TYPE);
556 }
557 
558 TEST_F(TestFileTable, ReadFileBadPath)
559 {
560     uint32_t fileHandle = 1;
561     uint32_t offset = 0;
562     uint32_t length = 0x4;
563 
564     std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_READ_FILE_REQ_BYTES>
565         requestMsg{};
566     auto requestMsgPtr = reinterpret_cast<pldm_msg*>(requestMsg.data());
567     auto payload_length = requestMsg.size() - sizeof(pldm_msg_hdr);
568     auto request = reinterpret_cast<pldm_read_file_req*>(requestMsg.data() +
569                                                          sizeof(pldm_msg_hdr));
570 
571     request->file_handle = fileHandle;
572     request->offset = offset;
573     request->length = length;
574 
575     using namespace pldm::filetable;
576     // Initialise the file table with 2 valid file handles 0 & 1.
577     auto& table = buildFileTable(fileTableConfig.c_str());
578 
579     // Invalid payload length
580     oem_ibm::Handler handler;
581     auto response = handler.readFile(requestMsgPtr, 0);
582     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
583     ASSERT_EQ(responsePtr->payload[0], PLDM_ERROR_INVALID_LENGTH);
584 
585     // Data out of range. File size is 1024, offset = 1024 is invalid.
586     request->offset = 1024;
587 
588     response = handler.readFile(requestMsgPtr, payload_length);
589     responsePtr = reinterpret_cast<pldm_msg*>(response.data());
590     ASSERT_EQ(responsePtr->payload[0], PLDM_DATA_OUT_OF_RANGE);
591 
592     // Invalid file handle
593     request->file_handle = 2;
594 
595     response = handler.readFile(requestMsgPtr, payload_length);
596     responsePtr = reinterpret_cast<pldm_msg*>(response.data());
597     ASSERT_EQ(responsePtr->payload[0], PLDM_INVALID_FILE_HANDLE);
598 
599     table.clear();
600 }
601 
602 TEST_F(TestFileTable, ReadFileGoodPath)
603 {
604     uint32_t fileHandle = 0;
605     uint32_t offset = 0;
606     uint32_t length = 0x4;
607 
608     std::array<uint8_t, sizeof(pldm_msg_hdr) + PLDM_READ_FILE_REQ_BYTES>
609         requestMsg{};
610     auto requestMsgPtr = reinterpret_cast<pldm_msg*>(requestMsg.data());
611     auto payload_length = requestMsg.size() - sizeof(pldm_msg_hdr);
612     auto request = reinterpret_cast<pldm_read_file_req*>(requestMsg.data() +
613                                                          sizeof(pldm_msg_hdr));
614 
615     request->file_handle = fileHandle;
616     request->offset = offset;
617     request->length = length;
618 
619     using namespace pldm::filetable;
620     // Initialise the file table with 2 valid file handles 0 & 1.
621     auto& table = buildFileTable(fileTableConfig.c_str());
622     FileEntry value{};
623     value = table.at(fileHandle);
624 
625     std::ifstream stream(value.fsPath, std::ios::in | std::ios::binary);
626     stream.seekg(offset);
627     std::vector<char> buffer(length);
628     stream.read(buffer.data(), length);
629 
630     oem_ibm::Handler handler;
631     auto responseMsg = handler.readFile(requestMsgPtr, payload_length);
632     auto response = reinterpret_cast<pldm_read_file_resp*>(
633         responseMsg.data() + sizeof(pldm_msg_hdr));
634     ASSERT_EQ(response->completion_code, PLDM_SUCCESS);
635     ASSERT_EQ(response->length, length);
636     ASSERT_EQ(0, memcmp(response->file_data, buffer.data(), length));
637 
638     // Test condition offset + length > fileSize;
639     size_t fileSize = 1024;
640     request->offset = 1023;
641     request->length = 10;
642 
643     stream.seekg(request->offset);
644     buffer.resize(fileSize - request->offset);
645     stream.read(buffer.data(), (fileSize - request->offset));
646 
647     responseMsg = handler.readFile(requestMsgPtr, payload_length);
648     response = reinterpret_cast<pldm_read_file_resp*>(responseMsg.data() +
649                                                       sizeof(pldm_msg_hdr));
650     ASSERT_EQ(response->completion_code, PLDM_SUCCESS);
651     ASSERT_EQ(response->length, (fileSize - request->offset));
652     ASSERT_EQ(0, memcmp(response->file_data, buffer.data(),
653                         (fileSize - request->offset)));
654 
655     table.clear();
656 }
657 
658 TEST_F(TestFileTable, WriteFileBadPath)
659 {
660     uint32_t fileHandle = 0;
661     uint32_t offset = 0;
662     uint32_t length = 0x10;
663 
664     std::vector<uint8_t> requestMsg(sizeof(pldm_msg_hdr) +
665                                     PLDM_WRITE_FILE_REQ_BYTES + length);
666     auto requestMsgPtr = reinterpret_cast<pldm_msg*>(requestMsg.data());
667     auto payload_length = requestMsg.size() - sizeof(pldm_msg_hdr);
668     auto request = reinterpret_cast<pldm_write_file_req*>(requestMsg.data() +
669                                                           sizeof(pldm_msg_hdr));
670 
671     using namespace pldm::filetable;
672     // Initialise the file table with 2 valid file handles 0 & 1.
673     auto& table = buildFileTable(fileTableConfig.c_str());
674 
675     request->file_handle = fileHandle;
676     request->offset = offset;
677     request->length = length;
678 
679     // Invalid payload length
680     oem_ibm::Handler handler;
681     auto response = handler.writeFile(requestMsgPtr, 0);
682     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
683     ASSERT_EQ(responsePtr->payload[0], PLDM_ERROR_INVALID_LENGTH);
684 
685     // Data out of range. File size is 1024, offset = 1024 is invalid.
686     request->offset = 1024;
687 
688     response = handler.writeFile(requestMsgPtr, payload_length);
689     responsePtr = reinterpret_cast<pldm_msg*>(response.data());
690     ASSERT_EQ(responsePtr->payload[0], PLDM_DATA_OUT_OF_RANGE);
691 
692     // Invalid file handle
693     request->file_handle = 2;
694 
695     response = handler.writeFile(requestMsgPtr, payload_length);
696     responsePtr = reinterpret_cast<pldm_msg*>(response.data());
697     ASSERT_EQ(responsePtr->payload[0], PLDM_INVALID_FILE_HANDLE);
698 
699     table.clear();
700 }
701 
702 TEST_F(TestFileTable, WriteFileGoodPath)
703 {
704     uint32_t fileHandle = 1;
705     uint32_t offset = 0;
706     std::array<uint8_t, 4> fileData = {0x41, 0x42, 0x43, 0x44};
707     uint32_t length = fileData.size();
708 
709     std::vector<uint8_t> requestMsg(sizeof(pldm_msg_hdr) +
710                                     PLDM_WRITE_FILE_REQ_BYTES + length);
711     auto requestMsgPtr = reinterpret_cast<pldm_msg*>(requestMsg.data());
712     auto payload_length = requestMsg.size() - sizeof(pldm_msg_hdr);
713     auto request = reinterpret_cast<pldm_write_file_req*>(requestMsg.data() +
714                                                           sizeof(pldm_msg_hdr));
715 
716     using namespace pldm::filetable;
717     // Initialise the file table with 2 valid file handles 0 & 1.
718     auto& table = buildFileTable(fileTableConfig.c_str());
719     FileEntry value{};
720     value = table.at(fileHandle);
721 
722     request->file_handle = fileHandle;
723     request->offset = offset;
724     request->length = length;
725     memcpy(request->file_data, fileData.data(), fileData.size());
726 
727     oem_ibm::Handler handler;
728     auto responseMsg = handler.writeFile(requestMsgPtr, payload_length);
729     auto response = reinterpret_cast<pldm_read_file_resp*>(
730         responseMsg.data() + sizeof(pldm_msg_hdr));
731 
732     std::ifstream stream(value.fsPath, std::ios::in | std::ios::binary);
733     stream.seekg(offset);
734     std::vector<char> buffer(length);
735     stream.read(buffer.data(), length);
736 
737     ASSERT_EQ(response->completion_code, PLDM_SUCCESS);
738     ASSERT_EQ(response->length, length);
739     ASSERT_EQ(0, memcmp(fileData.data(), buffer.data(), length));
740 
741     table.clear();
742 }
743 
744 TEST(writeFileByTypeFromMemory, testBadPath)
745 {
746     const auto hdr_size = sizeof(pldm_msg_hdr);
747     std::array<uint8_t, hdr_size + PLDM_RW_FILE_BY_TYPE_MEM_REQ_BYTES>
748         requestMsg{};
749     auto req = reinterpret_cast<pldm_msg*>(requestMsg.data());
750     size_t requestPayloadLength = requestMsg.size() - hdr_size;
751     struct pldm_read_write_file_by_type_memory_req* request =
752         reinterpret_cast<struct pldm_read_write_file_by_type_memory_req*>(
753             req->payload);
754     request->file_type = PLDM_FILE_TYPE_PEL;
755     request->file_handle = 0xFFFFFFFF;
756     request->offset = 0;
757     request->length = 17;
758     request->address = 0;
759 
760     oem_ibm::Handler handler;
761     auto response = handler.writeFileByTypeFromMemory(req, 0);
762     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
763 
764     struct pldm_read_write_file_by_type_memory_resp* resp =
765         reinterpret_cast<struct pldm_read_write_file_by_type_memory_resp*>(
766             responsePtr->payload);
767     ASSERT_EQ(PLDM_ERROR_INVALID_LENGTH, resp->completion_code);
768 
769     response = handler.writeFileByTypeFromMemory(req, requestPayloadLength);
770     responsePtr = reinterpret_cast<pldm_msg*>(response.data());
771 
772     resp = reinterpret_cast<struct pldm_read_write_file_by_type_memory_resp*>(
773         responsePtr->payload);
774     ASSERT_EQ(PLDM_INVALID_WRITE_LENGTH, resp->completion_code);
775 }
776 
777 TEST(getHandlerByType, allPaths)
778 {
779     uint32_t fileHandle{};
780     auto handler = getHandlerByType(PLDM_FILE_TYPE_PEL, fileHandle);
781     auto pelType = dynamic_cast<PelHandler*>(handler.get());
782     ASSERT_TRUE(pelType != nullptr);
783 
784     handler = getHandlerByType(PLDM_FILE_TYPE_LID_PERM, fileHandle);
785     auto lidType = dynamic_cast<LidHandler*>(handler.get());
786     ASSERT_TRUE(lidType != nullptr);
787     pelType = dynamic_cast<PelHandler*>(handler.get());
788     ASSERT_TRUE(pelType == nullptr);
789     handler = getHandlerByType(PLDM_FILE_TYPE_LID_TEMP, fileHandle);
790     lidType = dynamic_cast<LidHandler*>(handler.get());
791     ASSERT_TRUE(lidType != nullptr);
792 
793     using namespace sdbusplus::xyz::openbmc_project::Common::Error;
794     ASSERT_THROW(getHandlerByType(0xFFFF, fileHandle), InternalFailure);
795 }
796 
797 TEST(readFileByTypeIntoMemory, testBadPath)
798 {
799     const auto hdr_size = sizeof(pldm_msg_hdr);
800     std::array<uint8_t, hdr_size + PLDM_RW_FILE_BY_TYPE_MEM_REQ_BYTES>
801         requestMsg{};
802     auto req = reinterpret_cast<pldm_msg*>(requestMsg.data());
803     struct pldm_read_write_file_by_type_memory_req* request =
804         reinterpret_cast<struct pldm_read_write_file_by_type_memory_req*>(
805             req->payload);
806     request->file_type = 0xFFFF;
807     request->file_handle = 0;
808     request->offset = 0;
809     request->length = 17;
810     request->address = 0;
811 
812     oem_ibm::Handler handler;
813     auto response = handler.readFileByTypeIntoMemory(req, 0);
814     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
815     struct pldm_read_write_file_by_type_memory_resp* resp =
816         reinterpret_cast<struct pldm_read_write_file_by_type_memory_resp*>(
817             responsePtr->payload);
818     ASSERT_EQ(PLDM_ERROR_INVALID_LENGTH, resp->completion_code);
819 
820     response = handler.readFileByTypeIntoMemory(
821         req, PLDM_RW_FILE_BY_TYPE_MEM_REQ_BYTES);
822     responsePtr = reinterpret_cast<pldm_msg*>(response.data());
823     resp = reinterpret_cast<struct pldm_read_write_file_by_type_memory_resp*>(
824         responsePtr->payload);
825     ASSERT_EQ(PLDM_INVALID_WRITE_LENGTH, resp->completion_code);
826 
827     request->length = 16;
828     response = handler.readFileByTypeIntoMemory(
829         req, PLDM_RW_FILE_BY_TYPE_MEM_REQ_BYTES);
830     responsePtr = reinterpret_cast<pldm_msg*>(response.data());
831     resp = reinterpret_cast<struct pldm_read_write_file_by_type_memory_resp*>(
832         responsePtr->payload);
833     ASSERT_EQ(PLDM_INVALID_FILE_TYPE, resp->completion_code);
834 }
835 
836 TEST(readFileByType, testBadPath)
837 {
838     const auto hdr_size = sizeof(pldm_msg_hdr);
839     std::array<uint8_t, hdr_size + PLDM_RW_FILE_BY_TYPE_REQ_BYTES> requestMsg{};
840     auto payloadLength = requestMsg.size() - hdr_size;
841     auto req = reinterpret_cast<pldm_msg*>(requestMsg.data());
842     struct pldm_read_write_file_by_type_req* request =
843         reinterpret_cast<struct pldm_read_write_file_by_type_req*>(
844             req->payload);
845     request->file_type = 0xFFFF;
846     request->file_handle = 0;
847     request->offset = 0;
848     request->length = 13;
849 
850     oem_ibm::Handler handler;
851     auto response = handler.readFileByType(req, 0);
852     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
853     struct pldm_read_write_file_by_type_resp* resp =
854         reinterpret_cast<struct pldm_read_write_file_by_type_resp*>(
855             responsePtr->payload);
856     ASSERT_EQ(PLDM_ERROR_INVALID_LENGTH, resp->completion_code);
857 
858     response = handler.readFileByType(req, payloadLength);
859     responsePtr = reinterpret_cast<pldm_msg*>(response.data());
860     resp = reinterpret_cast<struct pldm_read_write_file_by_type_resp*>(
861         responsePtr->payload);
862     ASSERT_EQ(PLDM_INVALID_FILE_TYPE, resp->completion_code);
863 }
864 
865 TEST(readFileByType, testReadFile)
866 {
867     LidHandler handler(0, true);
868     Response response;
869     uint32_t length{};
870 
871     auto rc = handler.readFile({}, 0, length, response);
872     ASSERT_EQ(PLDM_INVALID_FILE_HANDLE, rc);
873 
874     char tmplt[] = "/tmp/lid.XXXXXX";
875     auto fd = mkstemp(tmplt);
876     std::vector<uint8_t> in = {100, 10, 56, 78, 34, 56, 79, 235, 111};
877     write(fd, in.data(), in.size());
878     close(fd);
879     length = in.size() + 1000;
880     rc = handler.readFile(tmplt, 0, length, response);
881     ASSERT_EQ(rc, PLDM_SUCCESS);
882     ASSERT_EQ(length, in.size());
883     ASSERT_EQ(response.size(), in.size());
884     ASSERT_EQ(std::equal(in.begin(), in.end(), response.begin()), true);
885 }
886