| #include "pdr_file.hpp" |
| |
| #include <libpldm/entity.h> |
| #include <sys/stat.h> |
| |
| #include <phosphor-logging/lg2.hpp> |
| |
| PHOSPHOR_LOG2_USING; |
| |
| namespace pldm |
| { |
| namespace responder |
| { |
| namespace pdr_file |
| { |
| |
| using Json = nlohmann::json; |
| |
| bool generateSingleFilePDR( |
| const Json& fileEntry, const std::string& directoryPath, |
| uint16_t entityType, uint16_t containerId, |
| const std::string& configFilePath, |
| const struct timespec& config_last_modified, |
| std::map<uint16_t, FileInfo>& fileIdentifierToFileInfo, |
| std::map<uint32_t, FileInfo>& recordHandleToFileInfo, |
| pdr_utils::RepoInterface& repo, uint32_t* recordHandleToUpdate) |
| { |
| auto fileIdIt = fileEntry.find("FileIdentifier"); |
| if (fileIdIt == fileEntry.end() || !fileIdIt->is_number()) |
| { |
| error("FileIdentifier is missing or it is not a number."); |
| return false; |
| } |
| |
| auto fileNameIt = fileEntry.find("FileName"); |
| if (fileNameIt == fileEntry.end() || !fileNameIt->is_string()) |
| { |
| error("FileName is missing or it is not a string."); |
| return false; |
| } |
| |
| auto fileSizeIt = fileEntry.find("FileSize"); |
| if (fileSizeIt == fileEntry.end() || !fileSizeIt->is_number()) |
| { |
| error("FileSize is missing or it is not a number."); |
| return false; |
| } |
| |
| const std::string& fileName = fileNameIt.value(); |
| int fileNameLengthWithNull = fileName.length() + 1; |
| |
| // Calculate the total length of the PDR including the common PDR |
| // header size. For now this skips the |
| // OemFileClassificationNameLength and OemFileClassificationName |
| // since they are not included. |
| size_t pdrSize = |
| PLDM_PDR_FILE_DESCRIPTOR_PDR_MIN_LENGTH + fileNameLengthWithNull; |
| pldm_platform_file_descriptor_pdr pdr{}; |
| |
| // This will be assigned automatically by PDR repository. |
| pdr.hdr.record_handle = (recordHandleToUpdate) ? *recordHandleToUpdate : 0; |
| pdr.hdr.version = 1; |
| pdr.hdr.type = PLDM_FILE_DESCRIPTOR_PDR; |
| pdr.hdr.record_change_num = 0; |
| pdr.hdr.length = pdrSize - sizeof(pldm_pdr_hdr); |
| |
| pdr.terminus_handle = TERMINUS_HANDLE; |
| pdr.file_identifier = *fileIdIt; |
| pdr.container.entity_type = entityType; |
| pdr.container.entity_instance_num = fileEntry.value("InstanceNumber", 0); |
| pdr.container.entity_container_id = containerId; |
| pdr.superior_directory_file_identifier = |
| fileEntry.value("SuperiorDirectoryFileIdentifier", 0); |
| pdr.file_classification = fileEntry.value("FileClassification", 0); |
| pdr.oem_file_classification = 0; |
| pdr.file_capabilities.value = fileEntry.value("FileCapabilities", 0); |
| pdr.file_maximum_size = fileSizeIt.value(); |
| pdr.file_maximum_file_descriptor_count = |
| fileEntry.value("MaxFileDescriptors", 0); |
| pdr.file_name.ptr = reinterpret_cast<const uint8_t*>(fileName.c_str()); |
| pdr.file_name.length = fileNameLengthWithNull; |
| pdr.oem_file_classification_name.ptr = nullptr; |
| pdr.oem_file_classification_name.length = 0; |
| |
| std::vector<uint8_t> entry{}; |
| entry.resize(pdrSize); |
| |
| size_t pdrSizeCopy = pdrSize; |
| int ret = encode_pldm_platform_file_descriptor_pdr(&pdr, entry.data(), |
| &pdrSizeCopy); |
| if (ret != 0) |
| { |
| error("Failed to encode file descriptor PDR for '{FILE_NAME}'", |
| "FILE_NAME", fileName); |
| return false; |
| } |
| |
| FileInfo fileInfo{}; |
| |
| // Try opening the file here. If the open fail, we will not save the |
| // PDR. If we do open the file later on, PDR information and the |
| // actual file might not be in sync. |
| fileInfo.filePath = directoryPath + "/" + fileName; |
| FILE* fp = fopen(fileInfo.filePath.c_str(), "rb"); |
| if (fp == nullptr) |
| { |
| error("Failed to open file '{FILE_PATH}'", "FILE_PATH", |
| fileInfo.filePath); |
| return false; |
| } |
| |
| // This file pointer will be kept open through out the lifetime of |
| // the program |
| // TODO: If the file changes during runtime, we need to load it |
| // again. At that time we will close this and open a file pointer to |
| // the new file. |
| fileInfo.fp = fp; |
| fileInfo.fileIdentifier = *fileIdIt; |
| fileInfo.fileSize = fileSizeIt.value(); |
| fileInfo.fileJsonEntry = fileEntry; |
| fileInfo.directoryPath = directoryPath; |
| fileInfo.entityType = entityType; |
| fileInfo.containerId = containerId; |
| fileInfo.configFilePath = configFilePath; |
| fileInfo.config_last_modified = config_last_modified; |
| struct stat fileStat; |
| if (stat(fileInfo.filePath.c_str(), &fileStat) == 0) |
| { |
| fileInfo.last_modified = fileStat.st_mtim; |
| } |
| else |
| { |
| error("Failed to stat file {FILE_PATH} during FileInfo initialization", |
| "FILE_PATH", fileInfo.filePath); |
| } |
| |
| pdr_utils::PdrEntry pdrEntry{}; |
| pdrEntry.data = entry.data(); |
| pdrEntry.size = pdrSize; |
| pdrEntry.handle.recordHandle = |
| (recordHandleToUpdate) ? *recordHandleToUpdate : 0; |
| pdr_utils::RecordHandle recordHandle; |
| |
| try |
| { |
| recordHandle = repo.addRecord(pdrEntry); |
| } |
| catch (const std::runtime_error& e) |
| { |
| error("Error adding record for file '{FILE_PATH}'", "FILE_PATH", |
| fileInfo.filePath); |
| return false; |
| } |
| |
| if (recordHandleToUpdate && (*recordHandleToUpdate != 0) && |
| (static_cast<uint32_t>(recordHandle) != *recordHandleToUpdate)) |
| { |
| // This indicates a problem with pldm_pdr_add not respecting the |
| // provided handle |
| error( |
| "Record handle mismatch for file '{FILE_PATH}': expected {EXPECTED}, got {GOT}", |
| "FILE_PATH", fileInfo.filePath, "EXPECTED", *recordHandleToUpdate, |
| "GOT", recordHandle); |
| } |
| |
| if (recordHandleToUpdate) |
| { |
| *recordHandleToUpdate = static_cast<uint32_t>(recordHandle); |
| } |
| |
| fileInfo.recordHandle = static_cast<uint32_t>(recordHandle); |
| |
| fileIdentifierToFileInfo.try_emplace(*fileIdIt, fileInfo); |
| recordHandleToFileInfo.try_emplace(static_cast<uint32_t>(recordHandle), |
| fileInfo); |
| |
| info( |
| "Added PDR record for file '{FILE_PATH}' with record handle {RECORD_HANDLE}", |
| "FILE_PATH", fileInfo.filePath, "RECORD_HANDLE", recordHandle); |
| |
| return true; |
| } |
| |
| void generateFileDescriptorPDR( |
| const Json& json, const std::string& configFilePath, |
| const struct timespec& config_last_modified, |
| std::map<uint16_t, FileInfo>& fileIdentifierToFileInfo, |
| std::map<uint32_t, FileInfo>& recordHandleToFileInfo, |
| pdr_utils::RepoInterface& repo) |
| { |
| static const std::vector<Json> emptyList{}; |
| auto entries = json.value("entries", emptyList); |
| for (const auto& e : entries) |
| { |
| auto entityTypeIt = e.find("EntityType"); |
| if (entityTypeIt == e.end() || !entityTypeIt->is_number()) |
| { |
| error("EntityType is missing or it is not a number."); |
| continue; |
| } |
| |
| uint16_t entityType = static_cast<uint16_t>(*entityTypeIt); |
| if ((entityType != PLDM_ENTITY_DEVICE_FILE) && |
| (entityType != PLDM_ENTITY_DEVICE_FILE_DIRECTORY)) |
| { |
| error( |
| "Invalid EntityType '{ENTITY_TYPE}' for file descriptor PDR. Expected 9 or 10.", |
| "ENTITY_TYPE", entityType); |
| continue; |
| } |
| |
| auto pathIt = e.find("Path"); |
| if (pathIt == e.end() || !pathIt->is_string()) |
| { |
| error("'Path' is missing or it is not a string."); |
| continue; |
| } |
| const std::string& directoryPath = pathIt->get<std::string>(); |
| |
| uint16_t container_id = e.value("container", 0); |
| |
| auto files = e.value("Files", emptyList); |
| for (const auto& file : files) |
| { |
| if (!generateSingleFilePDR( |
| file, directoryPath, entityType, container_id, |
| configFilePath, config_last_modified, |
| fileIdentifierToFileInfo, recordHandleToFileInfo, repo)) |
| { |
| error("Failed to generate PDR for a file entry in {PATH}", |
| "PATH", directoryPath); |
| } |
| } |
| } |
| } |
| |
| bool getFileEntryFromConfig(const std::string& configFilePath, |
| const std::string& fileName, |
| nlohmann::json& fileEntry) |
| { |
| try |
| { |
| auto json = pdr_utils::readJson(configFilePath); |
| if (json.empty()) |
| { |
| return false; |
| } |
| |
| static const nlohmann::json empty{}; |
| static const std::vector<nlohmann::json> emptyList{}; |
| auto filePdrs = json.value("fileDescriptorPDRs", empty); |
| for (const auto& filePdrEntries : filePdrs) |
| { |
| auto entries = filePdrEntries.value("entries", emptyList); |
| for (const auto& e : entries) |
| { |
| auto files = e.value("Files", emptyList); |
| for (const auto& file : files) |
| { |
| auto fileNameIt = file.find("FileName"); |
| if (fileNameIt != file.end() && fileNameIt->is_string()) |
| { |
| if (fileNameIt.value() == fileName) |
| { |
| fileEntry = file; |
| return true; |
| } |
| } |
| } |
| } |
| } |
| } |
| catch (const std::exception& e) |
| { |
| error( |
| "Failed to get file entry from config file {PATH}, error - {ERROR}", |
| "PATH", configFilePath, "ERROR", e); |
| } |
| return false; |
| } |
| |
| } // namespace pdr_file |
| } // namespace responder |
| } // namespace pldm |