Verified Commit 543671e1 authored by Karel Koci's avatar Karel Koci 🤘

Steal configuration files from not-installed packages

When package we are installing has configuration file same as some previously
installed, now uninstalled, package, we remove such configuration file from
not-installed package in favor new package.
parent c603599d
......@@ -622,6 +622,55 @@ function collision_check(current_status, remove_pkgs, add_pkgs)
return collisions, remove
Prepares table which is used for steal_config. Keys are all configuration files
of all not-installed packages. As values are tables with name of package (key pkg)
and hash (key hash).
function not_installed_confs(current_status)
local not_installed_confs = {}
for pkg, status in pairs(current_status) do
if status.Status[3] == "not-installed" then
for conf, hash in pairs(status.Conffiles) do
not_installed_confs[conf] = { pkg = pkg, hash = hash }
return not_installed_confs
Checks if configs aren't part of some not installed package. If such configuration
is located, it is removed from not installed package and if it is last config,
not-installed package entry is removed.
The current_status is what is returned from status_parse(). The not_installed_confs
is what not_installed_confs function returns. The configs is table of new
configuration files.
Returns table where key is configuration file name and value is hash.
function steal_configs(current_status, not_installed_confs, configs)
local steal = {}
-- Go trough all configs and check if they are not in not_installed_confs
for conf, _ in pairs(configs) do
if not_installed_confs[conf] then
local pkg = not_installed_confs[conf].pkg
DBG("Stealing \"" .. conf .. "\" from package " .. pkg)
steal[conf] = not_installed_confs[conf].hash
not_installed_confs[conf] = nil
-- Remove config from not-installed package
current_status[pkg].Conffiles[conf] = nil
-- Remove package if it has no other coffiles.
if not next(current_status[pkg].Conffiles) then
DBG("not-installed package " .. pkg .. " has no more conffiles, removing.")
current_status[pkg] = nil
return steal
-- Ensure the given directory exists
function dir_ensure(dir)
-- Try creating it.
......@@ -138,6 +138,9 @@ local function pkg_collision_check(status, to_remove, to_install)
local function pkg_move(status, plan, errors_collected)
-- Prepare table of not installed confs for config stealing
local not_installed_confs = backend.not_installed_confs(status)
local all_configs = {}
-- Go through the list once more and perform the prepared operations
for _, op in ipairs(plan) do
......@@ -153,6 +156,8 @@ local function pkg_move(status, plan, errors_collected)
script(errors_collected, op.control.Package, "preinst", "install", op.control.Version)
local steal = backend.steal_configs(status, not_installed_confs, op.configs)
utils.table_merge(op.old_configs, steal)
backend.pkg_merge_files(op.dir .. "/data", op.dirs, op.files, op.old_configs)
status[op.control.Package] = op.control
......@@ -475,6 +475,49 @@ function test_collisions()
}, rem)
-- Test config_steal and not_installed_confs function
function test_config_steal()
local status = B.status_parse()
-- Lets set dnsmasq-dhcpv6 as not installed
status["dnsmasq-dhcpv6"].Status[3] = "not-installed"
-- Prepare not_installed_confs table and check it
local not_installed_confs = B.not_installed_confs(status)
["/etc/config/dhcp"] = { pkg = "dnsmasq-dhcpv6", hash = "f81fe9bd228dede2165be71e5c9dcf76cc" },
["/etc/dnsmasq.conf"] = { pkg = "dnsmasq-dhcpv6", hash = "1e6ab19c1ae5e70d609ac7b6246541d520" }
}, not_installed_confs)
-- Now lets steal one of the configs
local stealed_confs = {
["/etc/config/dhcp"] = status["dnsmasq-dhcpv6"].Conffiles["/etc/config/dhcp"]
local steal = B.steal_configs(status, not_installed_confs, { ["/etc/config/dhcp"] = "hash" }) -- note that hash is not used
assert_table_equal(stealed_confs, steal)
-- Now lets steal second one
stealed_confs = {
["/etc/dnsmasq.conf"] = status["dnsmasq-dhcpv6"].Conffiles["/etc/dnsmasq.conf"]
steal = B.steal_configs(status, not_installed_confs, { ["/etc/dnsmasq.conf"] = "pkg_test2" })
assert_table_equal(stealed_confs, steal)
assert_nil(status["dnsmasq-dhcpv6"]) -- Now whole package should disappear
status = B.status_parse()
status["dnsmasq-dhcpv6"].Status[3] = "not-installed"
local not_installed_confs = B.not_installed_confs(status)
-- Lets try again but now with package that steals both config files
stealed_confs = {
["/etc/config/dhcp"] = status["dnsmasq-dhcpv6"].Conffiles["/etc/config/dhcp"],
["/etc/dnsmasq.conf"] = status["dnsmasq-dhcpv6"].Conffiles["/etc/dnsmasq.conf"]
steal = B.steal_configs(status, not_installed_confs, {
["/etc/config/dhcp"] = "hash",
["/etc/dnsmasq.conf"] = "hash",
["/etc/newone.conf"] = "hash"
assert_table_equal(stealed_confs, steal)
assert_nil(status["dnsmasq-dhcpv6"]) -- Whole package should disappear
function test_block_dump_ordered()
-- Empty block should produce empty output
assert_equal('', B.block_dump_ordered({}))
......@@ -25,7 +25,7 @@ files["src/lib/autoload/a_04_utils.lua"] = {
files["src/lib/autoload/a_06_backend.lua"] = {
globals = {
-- Functions and variables used in other files
"pkg_temp_dir", "repo_parse", "status_dump", "pkg_unpack", "pkg_examine", "collision_check", "dir_ensure", "pkg_merge_files", "pkg_merge_control", "pkg_config_info", "pkg_cleanup_files", "control_cleanup", "version_cmp", "flags_load", "flags_get", "script_run", "flags_get_ro", "flags_write", "flags_mark", "run_state",
"pkg_temp_dir", "repo_parse", "status_dump", "pkg_unpack", "pkg_examine", "collision_check", "not_installed_confs", "steal_configs", "dir_ensure", "pkg_merge_files", "pkg_merge_control", "pkg_config_info", "pkg_cleanup_files", "control_cleanup", "version_cmp", "flags_load", "flags_get", "script_run", "flags_get_ro", "flags_write", "flags_mark", "run_state",
-- Variables that we want to access from outside (ex. for testing purposes)
"status_file", "info_dir", "root_dir", "pkg_temp_dir", "flags_storage", "cmd_timeout", "cmd_kill_timeout", "stored_flags",
-- Functions that we want to access from outside (ex. for testing purposes)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment