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