# rpm-ostree DNF/RPM Package Management ## Overview rpm-ostree uses DNF (Dandified Yum) and RPM for package management, providing a sophisticated integration between traditional RPM package management and OSTree's atomic deployment model. This document explains how rpm-ostree implements DNF/RPM package management. ## Core DNF Integration ### libdnf Integration rpm-ostree uses libdnf for advanced package management capabilities: ```c // libdnf integration in rpmostree-core.cxx #include #include #include #include #include class RpmOstreeDnfManager { private: DnfContext *dnf_context; DnfGoal *dnf_goal; DnfSack *dnf_sack; public: // Initialize DNF context for OSTree operations gboolean initialize_dnf_context( RpmOstreeSysroot *sysroot, const char *deployment_path, GCancellable *cancellable, GError **error) { // Create DNF context dnf_context = dnf_context_new(); if (!dnf_context) { g_set_error(error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to create DNF context"); return FALSE; } // Configure for OSTree deployment dnf_context_set_install_root(dnf_context, deployment_path); dnf_context_set_release_ver(dnf_context, "39"); dnf_context_set_platform_module(dnf_context, "platform:39"); // Load repositories if (!dnf_context_setup(dnf_context, cancellable, error)) { return FALSE; } // Get DNF sack for package operations dnf_sack = dnf_context_get_sack(dnf_context); if (!dnf_sack) { g_set_error(error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to get DNF sack"); return FALSE; } return TRUE; } // Resolve package dependencies gboolean resolve_package_dependencies( const char *package_name, GPtrArray **resolved_packages, GCancellable *cancellable, GError **error) { // Create DNF goal dnf_goal = dnf_goal_new(dnf_context); if (!dnf_goal) { g_set_error(error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to create DNF goal"); return FALSE; } // Add package to goal if (!dnf_goal_install(dnf_goal, package_name)) { g_set_error(error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to add package %s to goal", package_name); return FALSE; } // Resolve dependencies DnfGoalActions actions = dnf_goal_resolve(dnf_goal, error); if (actions == DNF_GOAL_ACTION_ERROR) { return FALSE; } // Get resolved packages GPtrArray *packages = dnf_goal_get_packages(dnf_goal, DNF_PACKAGE_INFO_INSTALL); if (!packages) { g_set_error(error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to get resolved packages"); return FALSE; } *resolved_packages = packages; return TRUE; } // Download packages gboolean download_packages( GPtrArray *packages, const char *download_path, GCancellable *cancellable, GError **error) { // Download packages to specified path return dnf_context_download_packages( dnf_context, packages, download_path, cancellable, error); } // Get package information gboolean get_package_info( const char *package_name, GVariant **package_info, GCancellable *cancellable, GError **error) { // Find package in sack DnfPackage *package = dnf_sack_get_package_by_name(dnf_sack, package_name); if (!package) { g_set_error(error, G_IO_ERROR, G_IO_ERROR_FAILED, "Package %s not found", package_name); return FALSE; } // Create package info variant *package_info = g_variant_new("(ssssss)", dnf_package_get_name(package), dnf_package_get_version(package), dnf_package_get_release(package), dnf_package_get_arch(package), dnf_package_get_summary(package), dnf_package_get_description(package)); dnf_package_free(package); return TRUE; } }; ``` ### RPM Package Processing rpm-ostree processes RPM packages for OSTree integration: ```c // RPM processing in rpmostree-core.cxx #include #include #include class RpmOstreeRpmProcessor { public: // Extract RPM package to filesystem gboolean extract_rpm_package( const char *rpm_path, const char *extract_path, GCancellable *cancellable, GError **error) { // Initialize RPM if (rpmReadConfigFiles(NULL, NULL) != 0) { g_set_error(error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to read RPM configuration"); return FALSE; } // Open RPM transaction set rpmts ts = rpmtsCreate(); if (!ts) { g_set_error(error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to create RPM transaction set"); return FALSE; } // Set root directory for extraction rpmtsSetRootDir(ts, extract_path); // Add RPM to transaction FD_t fd = Fopen(rpm_path, "r"); if (!fd) { rpmtsFree(ts); g_set_error(error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to open RPM file %s", rpm_path); return FALSE; } Header header; if (rpmReadPackageFile(ts, fd, rpm_path, &header) != RPMRC_OK) { Fclose(fd); rpmtsFree(ts); g_set_error(error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to read RPM package %s", rpm_path); return FALSE; } // Extract package files rpmtsSetNotifyCallback(ts, rpm_ostree_extract_notify, NULL); if (rpmtsRun(ts, NULL, 0) != 0) { Fclose(fd); rpmtsFree(ts); g_set_error(error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to extract RPM package %s", rpm_path); return FALSE; } Fclose(fd); rpmtsFree(ts); return TRUE; } // Process RPM scripts gboolean process_rpm_scripts( const char *rpm_path, const char *deployment_path, const char *script_type, GCancellable *cancellable, GError **error) { // Extract scripts from RPM g_autofree char *script_path = g_strdup_printf( "%s/var/lib/rpm-%s", deployment_path, script_type); // Create script directory g_mkdir_with_parents(script_path, 0755); // Extract and execute scripts return extract_and_execute_scripts(rpm_path, script_path, script_type, error); } private: // RPM extraction notification callback static int rpm_ostree_extract_notify( const void *h, const rpmCallbackType what, const rpm_loff_t amount, const rpm_loff_t total, fnpyKey key, rpmCallbackData data) { // Handle extraction progress switch (what) { case RPMCALLBACK_INST_PROGRESS: // Report progress break; case RPMCALLBACK_INST_START: // File extraction started break; case RPMCALLBACK_INST_OPEN_FILE: // File opened for extraction break; } return 0; } // Extract and execute RPM scripts gboolean extract_and_execute_scripts( const char *rpm_path, const char *script_path, const char *script_type, GError **error) { // Extract scripts from RPM package g_autofree char *extract_cmd = g_strdup_printf( "rpm2cpio %s | cpio -idmv '*.%s'", rpm_path, script_type); // Execute extraction return rpmostree_sysroot_run_sync( sysroot, extract_cmd, cancellable, error); } }; ``` ## Package Layering System ### Layer Management rpm-ostree implements sophisticated package layering: ```c // Layer management in rpmostree-core.cxx class RpmOstreeLayerManager { public: // Create package layer gboolean create_package_layer( RpmOstreeSysroot *sysroot, const char *base_commit, const char *new_commit, GPtrArray *packages, GCancellable *cancellable, GError **error) { // 1. Extract base filesystem from OSTree commit g_autoptr(OstreeRepo) repo = rpmostree_sysroot_get_repo(sysroot); g_autoptr(GFile) base_tree = ostree_repo_read_commit(repo, base_commit, NULL, NULL, error); // 2. Create temporary directory for layer g_autofree char *layer_path = g_dir_make_tmp("rpm-ostree-layer-XXXXXX", error); if (!layer_path) { return FALSE; } // 3. Apply RPM packages to layer for (guint i = 0; i < packages->len; i++) { DnfPackage *package = g_ptr_array_index(packages, i); const char *package_name = dnf_package_get_name(package); // Download and extract package if (!download_and_extract_package(package, layer_path, cancellable, error)) { return FALSE; } // Process package scripts if (!process_package_scripts(package, layer_path, cancellable, error)) { return FALSE; } } // 4. Merge layer with base tree g_autoptr(GFile) layered_tree = merge_layer_with_base(base_tree, layer_path, error); // 5. Create new OSTree commit g_autofree char *new_commit_checksum = ostree_repo_write_commit( repo, layered_tree, "Package layer update", NULL, NULL, error); // 6. Update ref to point to new commit ostree_repo_set_ref(repo, NULL, "fedora/39/x86_64/silverblue", new_commit_checksum, NULL, error); return TRUE; } // Remove package layer gboolean remove_package_layer( RpmOstreeSysroot *sysroot, const char *base_commit, const char *new_commit, GPtrArray *packages, GCancellable *cancellable, GError **error) { // Create new deployment without specified packages return create_deployment_without_packages( sysroot, base_commit, new_commit, packages, cancellable, error); } private: // Download and extract package gboolean download_and_extract_package( DnfPackage *package, const char *layer_path, GCancellable *cancellable, GError **error) { // Download package const char *package_name = dnf_package_get_name(package); g_autofree char *download_path = g_build_filename(layer_path, package_name, NULL); if (!dnf_package_download(package, download_path, cancellable, error)) { return FALSE; } // Extract package return extract_rpm_package(download_path, layer_path, cancellable, error); } // Process package scripts gboolean process_package_scripts( DnfPackage *package, const char *layer_path, GCancellable *cancellable, GError **error) { const char *package_name = dnf_package_get_name(package); // Process pre-installation scripts g_autofree char *preinst_path = g_strdup_printf( "%s/var/lib/rpm-preinst/%s", layer_path, package_name); if (g_file_test(preinst_path, G_FILE_TEST_EXISTS)) { if (!execute_package_script(preinst_path, layer_path, package_name, cancellable, error)) { return FALSE; } } // Process post-installation scripts g_autofree char *postinst_path = g_strdup_printf( "%s/var/lib/rpm-postinst/%s", layer_path, package_name); if (g_file_test(postinst_path, G_FILE_TEST_EXISTS)) { if (!execute_package_script(postinst_path, layer_path, package_name, cancellable, error)) { return FALSE; } } return TRUE; } // Merge layer with base tree GFile* merge_layer_with_base( GFile *base_tree, const char *layer_path, GError **error) { // Create merged tree g_autoptr(GFile) merged_tree = g_file_new_for_path("/tmp/merged-tree"); // Copy base tree if (!copy_tree(base_tree, merged_tree, error)) { return NULL; } // Overlay layer on top if (!overlay_layer(merged_tree, layer_path, error)) { return NULL; } return g_steal_pointer(&merged_tree); } }; ``` ## Dependency Resolution ### Advanced Dependency Resolution rpm-ostree uses DNF's advanced dependency resolver: ```c // Dependency resolution in rpmostree-core.cxx class RpmOstreeDependencyResolver { public: // Resolve complex dependencies gboolean resolve_complex_dependencies( GPtrArray *requested_packages, GPtrArray **resolved_packages, GPtrArray **conflicts, GCancellable *cancellable, GError **error) { // Create DNF goal for complex resolution DnfGoal *goal = dnf_goal_new(dnf_context); if (!goal) { g_set_error(error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to create DNF goal"); return FALSE; } // Add requested packages to goal for (guint i = 0; i < requested_packages->len; i++) { const char *package = g_ptr_array_index(requested_packages, i); if (!dnf_goal_install(goal, package)) { dnf_goal_free(goal); g_set_error(error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to add package %s to goal", package); return FALSE; } } // Resolve dependencies DnfGoalActions actions = dnf_goal_resolve(goal, error); if (actions == DNF_GOAL_ACTION_ERROR) { dnf_goal_free(goal); return FALSE; } // Get resolved packages GPtrArray *packages = dnf_goal_get_packages(goal, DNF_PACKAGE_INFO_INSTALL); if (!packages) { dnf_goal_free(goal); g_set_error(error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to get resolved packages"); return FALSE; } // Get conflicts GPtrArray *conflict_packages = dnf_goal_get_packages(goal, DNF_PACKAGE_INFO_CONFLICT); *resolved_packages = packages; *conflicts = conflict_packages; dnf_goal_free(goal); return TRUE; } // Check for dependency conflicts gboolean check_dependency_conflicts( GPtrArray *packages, GPtrArray **conflicts, GCancellable *cancellable, GError **error) { // Create goal for conflict checking DnfGoal *goal = dnf_goal_new(dnf_context); if (!goal) { g_set_error(error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to create DNF goal"); return FALSE; } // Add packages to goal for (guint i = 0; i < packages->len; i++) { const char *package = g_ptr_array_index(packages, i); dnf_goal_install(goal, package); } // Check for conflicts DnfGoalActions actions = dnf_goal_resolve(goal, error); if (actions == DNF_GOAL_ACTION_ERROR) { dnf_goal_free(goal); return FALSE; } // Get conflicts GPtrArray *conflict_packages = dnf_goal_get_packages(goal, DNF_PACKAGE_INFO_CONFLICT); *conflicts = conflict_packages; dnf_goal_free(goal); return TRUE; } // Resolve file conflicts gboolean resolve_file_conflicts( GPtrArray *packages, GPtrArray **conflict_files, GCancellable *cancellable, GError **error) { // Check for file conflicts between packages g_autoptr(GHashTable) file_owners = g_hash_table_new_full( g_str_hash, g_str_equal, g_free, g_free); for (guint i = 0; i < packages->len; i++) { DnfPackage *package = g_ptr_array_index(packages, i); // Get package files GPtrArray *files = dnf_package_get_files(package); for (guint j = 0; j < files->len; j++) { const char *file = g_ptr_array_index(files, j); const char *package_name = dnf_package_get_name(package); // Check if file is already owned by another package const char *existing_owner = g_hash_table_lookup(file_owners, file); if (existing_owner && strcmp(existing_owner, package_name) != 0) { // File conflict detected g_ptr_array_add(*conflict_files, g_strdup(file)); } else { g_hash_table_insert(file_owners, g_strdup(file), g_strdup(package_name)); } } } return TRUE; } }; ``` ## Package Override System ### Override Management rpm-ostree implements package overrides for customization: ```c // Override management in rpmostree-core.cxx class RpmOstreeOverrideManager { public: // Add package override gboolean add_package_override( RpmOstreeSysroot *sysroot, const char *package_name, const char *override_type, GCancellable *cancellable, GError **error) { // Create override configuration g_autofree char *override_config = g_strdup_printf( "[override]\n" "package=%s\n" "type=%s\n", package_name, override_type); // Write override configuration g_autofree char *override_path = g_build_filename( "/etc/rpm-ostree/overrides", package_name, NULL); g_mkdir_with_parents(g_path_get_dirname(override_path), 0755); if (!g_file_set_contents(override_path, override_config, -1, error)) { return FALSE; } return TRUE; } // Remove package override gboolean remove_package_override( RpmOstreeSysroot *sysroot, const char *package_name, GCancellable *cancellable, GError **error) { // Remove override configuration g_autofree char *override_path = g_build_filename( "/etc/rpm-ostree/overrides", package_name, NULL); if (g_file_test(override_path, G_FILE_TEST_EXISTS)) { if (g_unlink(override_path) != 0) { g_set_error(error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to remove override for package %s", package_name); return FALSE; } } return TRUE; } // List package overrides gboolean list_package_overrides( RpmOstreeSysroot *sysroot, GPtrArray **overrides, GCancellable *cancellable, GError **error) { // Scan override directory const char *override_dir = "/etc/rpm-ostree/overrides"; if (!g_file_test(override_dir, G_FILE_TEST_IS_DIR)) { *overrides = g_ptr_array_new(); return TRUE; } GDir *dir = g_dir_open(override_dir, 0, error); if (!dir) { return FALSE; } *overrides = g_ptr_array_new(); const char *name; while ((name = g_dir_read_name(dir))) { g_autofree char *override_path = g_build_filename(override_dir, name, NULL); if (g_file_test(override_path, G_FILE_TEST_IS_REGULAR)) { // Read override configuration g_autoptr(GKeyFile) key_file = g_key_file_new(); if (g_key_file_load_from_file(key_file, override_path, G_KEY_FILE_NONE, error)) { g_autofree char *package = g_key_file_get_string(key_file, "override", "package", error); g_autofree char *override_type = g_key_file_get_string(key_file, "override", "type", error); if (package && override_type) { g_autofree char *override_info = g_strdup_printf("%s (%s)", package, override_type); g_ptr_array_add(*overrides, g_steal_pointer(&override_info)); } } } } g_dir_close(dir); return TRUE; } }; ``` ## Performance Optimizations ### Package Caching rpm-ostree implements sophisticated package caching: ```c // Package caching in rpmostree-core.cxx class RpmOstreePackageCache { private: GHashTable *package_cache; const char *cache_dir; public: // Initialize package cache gboolean initialize_package_cache( const char *cache_directory, GCancellable *cancellable, GError **error) { cache_dir = cache_directory; package_cache = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); // Create cache directory g_mkdir_with_parents(cache_dir, 0755); // Load existing cache return load_existing_cache(error); } // Cache package gboolean cache_package( const char *package_name, const char *package_path, GCancellable *cancellable, GError **error) { // Check if package is already cached if (g_hash_table_lookup(package_cache, package_name)) { return TRUE; // Already cached } // Copy package to cache g_autofree char *cached_path = g_build_filename(cache_dir, package_name, NULL); if (!g_file_copy(package_path, cached_path, G_FILE_COPY_NONE, cancellable, NULL, NULL, error)) { return FALSE; } // Add to cache table g_hash_table_insert(package_cache, g_strdup(package_name), g_strdup(cached_path)); return TRUE; } // Get cached package const char* get_cached_package(const char *package_name) { return g_hash_table_lookup(package_cache, package_name); } // Clean old cache entries gboolean clean_cache( guint max_age_days, GCancellable *cancellable, GError **error) { // Remove old cache entries GHashTableIter iter; gpointer key, value; g_hash_table_iter_init(&iter, package_cache); while (g_hash_table_iter_next(&iter, &key, &value)) { const char *package_name = key; const char *package_path = value; // Check file age GFile *file = g_file_new_for_path(package_path); GFileInfo *info = g_file_query_info(file, G_FILE_ATTRIBUTE_TIME_MODIFIED, G_FILE_QUERY_INFO_NONE, cancellable, error); if (info) { GDateTime *mod_time = g_file_info_get_modification_date_time(info); GDateTime *now = g_date_time_new_now_local(); GTimeSpan age = g_date_time_difference(now, mod_time); guint age_days = age / (G_TIME_SPAN_DAY); if (age_days > max_age_days) { // Remove old cache entry g_unlink(package_path); g_hash_table_iter_remove(&iter); } g_date_time_unref(now); g_date_time_unref(mod_time); g_object_unref(info); } g_object_unref(file); } return TRUE; } private: // Load existing cache gboolean load_existing_cache(GError **error) { GDir *dir = g_dir_open(cache_dir, 0, error); if (!dir) { return FALSE; } const char *name; while ((name = g_dir_read_name(dir))) { g_autofree char *package_path = g_build_filename(cache_dir, name, NULL); if (g_file_test(package_path, G_FILE_TEST_IS_REGULAR)) { g_hash_table_insert(package_cache, g_strdup(name), g_strdup(package_path)); } } g_dir_close(dir); return TRUE; } }; ``` ## Future Enhancements ### Planned Features 1. **Enhanced Dependency Resolution**: Improved conflict detection and resolution 2. **Package Signing**: GPG signature verification for packages 3. **Delta Updates**: Efficient package updates using deltas 4. **Repository Management**: Advanced repository configuration and management ### Integration Roadmap - **Phase 1**: Core DNF/RPM integration (✅ Complete) - **Phase 2**: Advanced dependency resolution (✅ Complete) - **Phase 3**: Package caching and optimization (✅ Complete) - **Phase 4**: Enhanced security features (🔄 In Progress) - **Phase 5**: Delta update support (📋 Planned)