Files
dpm-core/modules/build/src/metadata.cpp

186 lines
6.2 KiB
C++

/**
* @file metadata.cpp
* @brief Implementation of DPM package metadata functions
*
* Implements functions for creating and manipulating DPM package metadata.
*
* @copyright Copyright (c) 2025 SILO GROUP LLC
* @author Chris Punches <chris.punches@silogroup.org>
*
* Part of the Dark Horse Linux Package Manager (DPM)
*/
#include "metadata.hpp"
bool metadata_generate_new(
const std::filesystem::path& package_dir,
const std::string& package_name,
const std::string& package_version,
const std::string& architecture
) {
try {
std::filesystem::path metadata_dir = package_dir / "metadata";
// Create NAME file
{
std::ofstream name_file(metadata_dir / "NAME");
if (name_file.is_open()) {
name_file << package_name;
name_file.close();
}
}
// Create VERSION file
{
std::ofstream version_file(metadata_dir / "VERSION");
if (version_file.is_open()) {
version_file << package_version;
version_file.close();
}
}
// Create ARCHITECTURE file
{
std::ofstream arch_file(metadata_dir / "ARCHITECTURE");
if (arch_file.is_open()) {
arch_file << architecture;
arch_file.close();
}
}
// Create empty placeholder files for other metadata
std::vector<std::string> metadata_files = {
"AUTHOR",
"MAINTAINER",
"DEPENDENCIES",
"DESCRIPTION",
"CONTENTS_MANIFEST_DIGEST",
"LICENSE",
"PACKAGE_DIGEST",
"HOOKS_DIGEST",
"PROVIDES",
"REPLACES",
"SOURCE",
"CHANGELOG"
};
for (const auto& file_name : metadata_files) {
std::ofstream metadata_file(metadata_dir / file_name);
metadata_file.close();
}
dpm_log(LOG_INFO, "Created metadata files");
// Update the contents manifest
if (!generate_contents_manifest(package_dir)) {
dpm_log(LOG_ERROR, "Failed to update contents manifest");
return false;
}
return true;
} catch (const std::exception& e) {
dpm_log(LOG_ERROR, ("Failed to create metadata files: " + std::string(e.what())).c_str());
return false;
}
}
/**
* @brief Updates the contents manifest file for a package
*
* Creates the CONTENTS_MANIFEST_DIGEST file by scanning the contents directory
* and generating a line for each file with control designation,
* checksum, permissions, ownership, and path information.
*
* @param package_dir Root directory of the package being staged
* @return true if manifest generation was successful, false otherwise
*/
bool generate_contents_manifest(const std::filesystem::path& package_dir)
{
try {
std::filesystem::path contents_dir = package_dir / "contents";
std::filesystem::path manifest_path = package_dir / "metadata" / "CONTENTS_MANIFEST_DIGEST";
// Log which hash algorithm is being used
std::string hash_algorithm = get_configured_hash_algorithm();
dpm_log(LOG_INFO, ("Generating contents manifest using " + hash_algorithm + " checksums...").c_str());
// Open manifest file for writing
std::ofstream manifest_file(manifest_path);
if (!manifest_file.is_open()) {
dpm_log(LOG_ERROR, ("Failed to open manifest file for writing: " + manifest_path.string()).c_str());
return false;
}
// Process each file in the contents directory recursively
for (const auto& entry : std::filesystem::recursive_directory_iterator(contents_dir)) {
// Skip directories, we only need to record files
if (std::filesystem::is_directory(entry)) {
continue;
}
// Get file information
std::filesystem::path file_path = entry.path();
std::filesystem::path relative_path = std::filesystem::relative(file_path, contents_dir);
std::string absolute_path = "/" + relative_path.string(); // Add leading slash
// Get file stats for permissions
struct stat file_stat;
if (stat(file_path.c_str(), &file_stat) != 0) {
dpm_log(LOG_FATAL, ("Failed to get file stats for: " + file_path.string()).c_str());
return false;
}
// Format permissions as octal
char perms[5];
snprintf(perms, sizeof(perms), "%04o", file_stat.st_mode & 07777);
// Get owner and group information
struct passwd* pw = getpwuid(file_stat.st_uid);
struct group* gr = getgrgid(file_stat.st_gid);
std::string owner;
if (pw) {
owner = pw->pw_name;
} else {
owner = std::to_string(file_stat.st_uid);
}
std::string group;
if (gr) {
group = gr->gr_name;
} else {
group = std::to_string(file_stat.st_gid);
}
std::string ownership = owner + ":" + group;
// Calculate file checksum using the configured algorithm
std::string checksum = generate_file_checksum(file_path);
if (checksum.empty()) {
dpm_log(LOG_FATAL, ("Failed to generate checksum for: " + file_path.string()).c_str());
return false;
}
// By default, mark all files as controlled ('C')
char control_designation = 'C';
// Write the manifest entry
// Format: control_designation checksum permissions owner:group /absolute/path
manifest_file << control_designation << " "
<< checksum << " "
<< perms << " "
<< ownership << " "
<< absolute_path << "\n";
}
manifest_file.close();
return true;
}
catch (const std::exception& e) {
dpm_log(LOG_ERROR, ("Failed to generate contents manifest: " + std::string(e.what())).c_str());
return false;
}
}