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