blob: ef4545cd3272f3315252edc199d2d71e171166cd [file]
#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