Integrate modification config check to removal

Check if config file has been modified when considering to remove it. If
it has, leave it in place.
parent d65755d0
......@@ -108,4 +108,11 @@ function clone(data)
end
end
-- Add all elements of src to dest
function table_merge(dest, src)
for k, v in pairs(src) do
dest[k] = v
end
end
return _M
......@@ -668,41 +668,46 @@ end
Remove files provided as a set and any directories which became
empty by doing so (recursively).
]]
function pkg_cleanup_files(files)
function pkg_cleanup_files(files, rm_configs)
for f in pairs(files) do
-- Make sure there are no // in there, which would confuse the directory cleaning code
f = f:gsub("/+", "/")
path = root_dir .. f
DBG("Removing file " .. path)
local ok, err = pcall(function () os.remove(path) end)
-- If it failed because the file didn't exist, that's OK. Mostly.
if not ok then
local tp = stat(path)
if tp then
error(err)
else
WARN("Not removing " .. path .. " since it is not there")
local path = root_dir .. f
local hash = rm_configs[f]
if hash and config_modified(path, hash) then
DBG("Not removing config " .. f .. ", as it has been modified")
else
DBG("Removing file " .. path)
local ok, err = pcall(function () os.remove(path) end)
-- If it failed because the file didn't exist, that's OK. Mostly.
if not ok then
local tp = stat(path)
if tp then
error(err)
else
WARN("Not removing " .. path .. " since it is not there")
end
end
end
-- Now, go through the levels of f, looking if they may be removed
-- Iterator for the chunking of the path
function get_parent()
local parent = f:match("^(.+)/[^/]+")
f = parent
return f
end
for parent in get_parent do
if next(ls(root_dir .. parent)) then
DBG("Directory " .. root_dir .. parent .. " not empty, keeping in place")
-- It is not empty
break
else
DBG("Removing empty directory " .. root_dir .. parent)
local ok, err = pcall(function () os.remove(root_dir .. parent) end)
if not ok then
-- It is an error, but we don't want to give up on the rest of the operation because of that
ERROR("Failed to removed empty " .. parent .. ", ignoring")
-- Now, go through the levels of f, looking if they may be removed
-- Iterator for the chunking of the path
function get_parent()
local parent = f:match("^(.+)/[^/]+")
f = parent
return f
end
for parent in get_parent do
if next(ls(root_dir .. parent)) then
DBG("Directory " .. root_dir .. parent .. " not empty, keeping in place")
-- It is not empty
break
else
DBG("Removing empty directory " .. root_dir .. parent)
local ok, err = pcall(function () os.remove(root_dir .. parent) end)
if not ok then
-- It is an error, but we don't want to give up on the rest of the operation because of that
ERROR("Failed to removed empty " .. parent .. ", ignoring")
break
end
end
end
end
......
......@@ -29,6 +29,7 @@ local ipairs = ipairs
local next = next
local error = error
local pcall = pcall
local pairs = pairs
local table = table
local backend = require "backend"
local utils = require "utils"
......@@ -105,10 +106,13 @@ function perform(operations)
-- TODO: Format the error message about collisions
error("Collisions happened")
end
local all_configs = {}
-- TODO: Journal note, we're going to proceed now.
-- Go through the list once more and perform the prepared operations
for _, op in ipairs(plan) do
if op.op == "install" then
-- We may want to remove one of the old configs on upgrade. Store the hash to check modification
utils.table_merge(all_configs, op.old_configs)
-- TODO: pre-install scripts (who would use such thing anyway?)
backend.pkg_merge_files(op.dir .. "/data", op.dirs, op.files, op.old_configs)
end
......@@ -121,12 +125,13 @@ function perform(operations)
status[op.control.Package] = op.control
-- TODO: Postinst script
elseif op.op == "remove" then
utils.table_merge(all_configs, status[op.name].Conffiles or {})
status[op.name] = nil
-- TODO: Pre-rm script, but only if not re-installed
end
end
-- Clean up the files from removed or upgraded packages
backend.pkg_cleanup_files(removes)
backend.pkg_cleanup_files(removes, all_configs)
-- TODO: post-rm scripts, for the removed (not re-installed) packages
-- TODO: Think about when to clean up any leftover files if something goes wrong? On success? On transaction rollback as well?
end)
......
......@@ -394,10 +394,27 @@ function test_pkg_unpack()
-- Delete the backed-up file, it is not tracked
os.remove(test_root .. "/etc/config/updater-opkg")
-- Now try clearing the package. When we list all the files, it should remove the directories as well.
B.pkg_cleanup_files(files)
B.pkg_cleanup_files(files, {})
assert_table_equal({}, ls(test_root))
end
function test_cleanup_files_config()
local test_root = mkdtemp()
table.insert(tmp_dirs, test_root)
-- Create an empty testing file
local fname = test_root .. "/config"
io.open(fname, "w"):close()
B.root_dir = test_root
-- First try with a non-matching hash ‒ the file has been modified
B.pkg_cleanup_files({["/config"] = true}, {["/config"] = "12345678901234567890123456789012"})
-- It is left there
assert_equal("r", stat(fname))
-- But when it matches, it is removed
B.pkg_cleanup_files({["/config"] = true}, {["/config"] = "d41d8cd98f00b204e9800998ecf8427e"})
-- The file disappeared
assert_nil(stat(fname))
end
-- Test the collision_check function
function test_collisions()
local status = B.status_parse()
......
......@@ -26,7 +26,10 @@ module("transaction-tests", package.seeall, lunit.testcase)
local test_status = {
["pkg-rem"] = {
Package = "pkg-rem"
Package = "pkg-rem",
Conffiles = {
remconf = "12345678901234567890123456789012"
}
},
["pkg-name"] = {
Package = "pkg-name",
......@@ -118,7 +121,7 @@ function test_perform_empty()
},
{
f = "backend.pkg_cleanup_files",
p = {{}}
p = {{}, {}}
}
}, outro({}, test_status))
assert_table_equal(expected, mocks_called)
......@@ -174,7 +177,7 @@ function test_perform_ok()
},
{
f = "backend.pkg_cleanup_files",
p = {{d2 = true}}
p = {{d2 = true}, {c = "12345678901234567890123456789012", remconf = "12345678901234567890123456789012"}}
}
}, outro({"pkg_dir"}, status_mod))
assert_table_equal(expected, mocks_called)
......
......@@ -67,3 +67,14 @@ function test_clone()
assert_not_equal(input.z, output.z)
assert_equal("xyz", U.clone("xyz"))
end
function test_table_merge()
local t1 = {}
local t2 = {a = 1, b = 2}
U.table_merge(t1, t2)
assert_table_equal(t2, t1)
U.table_merge(t1, {})
assert_table_equal(t2, t1)
U.table_merge(t1, {b = 3, c = 4})
assert_table_equal({a = 1, b = 3, c = 4}, t1)
end
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