Verified Commit 8876cbcb authored by Boleslav Březovský's avatar Boleslav Březovský Committed by Boleslav Brezovsky

Backup user changed non-config files

parent 22cf0058
......@@ -43,23 +43,23 @@ local stat = stat
local lstat = lstat
local mkdir = mkdir
local move = move
local copy = copy
local ls = ls
local md5_file = md5_file
local sha256_file = sha256_file
local DBG = DBG
local WARN = WARN
local INFO = INFO
local utils = require "utils"
local locks = require "locks"
module "backend"
-- Functions and variables used in other files
-- luacheck: globals pkg_temp_dir repo_parse status_dump pkg_unpack pkg_examine collision_check installed_confs steal_configs dir_ensure pkg_merge_files pkg_merge_control pkg_config_info pkg_cleanup_files control_cleanup version_cmp version_match script_run run_state
-- luacheck: globals pkg_temp_dir repo_parse status_dump pkg_unpack pkg_examine collision_check installed_confs steal_configs dir_ensure pkg_merge_files pkg_merge_control pkg_config_info pkg_cleanup_files control_cleanup version_cmp version_match script_run run_state user_path_move
-- Variables that we want to access from outside (ex. for testing purposes)
-- luacheck: globals status_file info_dir root_dir pkg_temp_dir cmd_timeout cmd_kill_timeout dir_opkg_collided
-- Functions that we want to access from outside (ex. for testing purposes)
-- luacheck: globals root_dir_set block_parse block_split block_dump_ordered pkg_status_dump package_postprocess status_parse get_parent config_modified
-- luacheck: globals root_dir_set block_parse block_split block_dump_ordered pkg_status_dump package_postprocess status_parse get_parent config_modified get_nonconf_files get_changed_files pkg_backup_files
--[[
Configuration of the module. It is supported (yet unlikely to be
......@@ -77,13 +77,39 @@ root_dir = "/"
local pkg_temp_dir_suffix = "/usr/share/updater/unpacked"
pkg_temp_dir = pkg_temp_dir_suffix
-- Directory where we move files and directories that weren't part of any package.
local dir_opkg_collided_suffix = "/var/opkg-collided"
local dir_opkg_collided_suffix = "/usr/share/updater/collided"
dir_opkg_collided = dir_opkg_collided_suffix
-- Time after which we SIGTERM external commands. Something incredibly long, just prevent them from being stuck.
cmd_timeout = 600000
-- Time after which we SIGKILL external commands
cmd_kill_timeout = 900000
--[[
Move anything on given path to dir_opkg_collided. This backups and removes original files.
When keep is set to TRUE, file is copied instead of moved
]]
local function user_path_move(path, keep)
-- At first create same parent directory relative to dir_opkg_collided
local fpath = ""
for dir in (dir_opkg_collided .. path):gsub("[^/]*/?$", ""):gmatch("[^/]+") do
local randex = ""
while not dir_ensure(fpath .. "/" .. dir .. randex) do
-- If there is file with same name, then append some random extension
randex = "." .. utils.randstr(6)
end
fpath = fpath .. "/" .. dir .. randex
end
WARN("Collision with existing path. Moving " .. path .. " to " .. fpath)
-- fpath is directory so path will be placed to that directory
-- If in fpath is file of same name, then it is replaced. And if there is
-- directory of same name then it is placed inside. But lets not care.
if keep then
copy(path, fpath)
else
move(path, fpath)
end
end
--[[
Set all the configurable directories to be inside the provided dir
Effectively sets that the whole system is mounted under some
......@@ -342,6 +368,43 @@ local function merge(target, additions)
end
end
-- Return table of package files with hashes. Configuration files are excluded.
function get_nonconf_files(pkg)
local pkg_path = info_dir .. pkg.Package
local files = {}
local md5sum_file = io.open(pkg_path .. ".files-md5sum")
if md5sum_file then
for line in md5sum_file:lines() do
local hash, name = line:match('^(%S+)%s+(%S+)$')
if name and hash then
files[name] = hash
end
end
md5sum_file:close()
end
local conffile = io.open(pkg_path .. ".conffiles")
if conffile then
for filename in conffile:lines() do
files[filename] = nil -- remove config file from files
end
conffile:close()
end
return files
end
function get_changed_files(files)
local changed = {}
for filename, hash in pairs(files) do
if utils.file_exists(filename) then
local filehash = md5_file(filename)
if filehash ~= hash then
table.insert(changed, filename)
end
end
end
return changed
end
function status_parse()
DBG("Parsing status file ", status_file)
local result = {}
......@@ -357,6 +420,9 @@ function status_parse()
merge(pkg, pkg_control(pkg.Package))
pkg.files = pkg_files(pkg.Package)
end
-- Get list of changed files (without config files)
-- and put it into the journal
pkg.ChangedFiles = get_changed_files(get_nonconf_files(pkg))
pkg = package_postprocess(pkg)
result[pkg.Package] = pkg
end
......@@ -768,25 +834,6 @@ function dir_ensure(dir)
return true
end
-- Move anything on given path to dir_opkg_collided. This backups and removes original files.
local function user_path_move(path)
-- At first create same parent directory relative to dir_opkg_collided
local fpath = ""
for dir in (dir_opkg_collided .. path):gsub("[^/]*/?$", ""):gmatch("[^/]+") do
local randex = ""
while not dir_ensure(fpath .. "/" .. dir .. randex) do
-- If there is file with same name, then append some random extension
randex = "." .. utils.randstr(6)
end
fpath = fpath .. "/" .. dir .. randex
end
WARN("Collision with existing path. Moving " .. path .. " to " .. fpath)
-- fpath is directory so path will be placed to that directory
-- If in fpath is file of same name, then it is replaced. And if there is
-- directory of same name then it is placed inside. But lets not care.
move(path, fpath)
end
--[[
Merge the given package into the live system and remove the temporary directory.
......@@ -1045,7 +1092,6 @@ is returned.
]]
function config_modified(file, hash)
DBG("Checking if file " .. file .. " is modified against " .. hash)
INFO("Checking if file " .. file .. " is modified against " .. hash)
local len = hash:len()
local hasher
if len == 32 then
......@@ -1068,8 +1114,7 @@ function config_modified(file, hash)
if utils.file_exists(file) then
local got = hasher(file):lower()
hash = hash:lower()
DBG("Hashes: " .. got .. " " .. hash)
INFO("Hashes: " .. got .. " " .. hash)
DBG("Hashes: for " .. file .. ": " .. got .. " " .. hash)
return hasher(file):lower() ~= hash:lower()
else
return nil
......
......@@ -111,6 +111,7 @@ local function pkg_unpack(operations, status)
else
old_configs = configs or {}
end
local changed_files = utils.multi_index(status, control.Package, "changed_files")
table.insert(plan, {
op = "install",
dir = pkg_dir,
......@@ -118,6 +119,7 @@ local function pkg_unpack(operations, status)
dirs = dirs,
configs = configs,
old_configs = old_configs,
changed_files = changed_files,
control = control,
reboot_immediate = op.reboot == "immediate"
})
......@@ -182,6 +184,9 @@ local function pkg_move(status, plan, early_remove, errors_collected)
if early_remove[op.control.Package] then
backend.pkg_cleanup_files(early_remove[op.control.Package], all_configs)
end
for _, path in pairs(utils.multi_index(status, op.control.Package, 'ChangedFiles') or {}) do
backend.user_path_move(path, true)
end
local did_merge = backend.pkg_merge_files(op.dir .. "/data", op.dirs, op.files, op.old_configs)
status[op.control.Package] = op.control
if op.reboot_immediate and did_merge then -- we reboot only if we did merge, if files were already merged then we already rebooted.
......
......@@ -513,6 +513,29 @@ static int lua_move(lua_State *L) {
return 0;
}
static int lua_copy(lua_State *L) {
const char *old = luaL_checkstring(L, 1);
const char *new = luaL_checkstring(L, 2);
/*
* NOTE:
* This is blatant copy of `lua_move`, therefore same structures are used
* named `mv_*` instead of `cp_*` that would make more sense.
* Both functions will be replaced once we start using Lua Posix library
* so it's just a temporary solution.
*/
struct events *events = extract_registry(L, "events");
ASSERT(events);
struct mv_result_data mv_result_data = { .err = NULL };
struct wait_id id = run_util(events, mv_result, NULL, &mv_result_data, 0, NULL, -1, -1, "cp", "-f", old, new, (const char *)NULL);
events_wait(events, 1, &id);
if (mv_result_data.status) {
lua_pushfstring(L, "Failed to copy '%s' to '%s': %s (ecode %d)", old, new, mv_result_data.err, mv_result_data.status);
free(mv_result_data.err);
return lua_error(L);
}
return 0;
}
static const char *stat2str(const struct stat *buf) {
switch (buf->st_mode & S_IFMT) {
case S_IFSOCK:
......@@ -807,6 +830,7 @@ static const struct injected_func injected_funcs[] = {
{ lua_getcwd, "getcwd" },
{ lua_mkdir, "mkdir" },
{ lua_move, "move" },
{ lua_copy, "copy" },
{ lua_ls, "ls" },
{ lua_stat, "stat" },
{ lua_lstat, "lstat" },
......
......@@ -18,6 +18,7 @@ along with Updater. If not, see <http://www.gnu.org/licenses/>.
]]--
require 'lunit'
local print = print
local B = require 'backend'
require 'utils'
......@@ -117,6 +118,7 @@ function test_package_postprocces()
Depends = "libc, kernel (= 3.18.21-1-70ea6b9a4b789c558ac9d579b5c1022f-10), kmod-nls-base",
Status = "install user installed",
Architecture = "mpc85xx",
ChangedFiles = {},
Conffiles =
[[
/etc/config/dhcp f81fe9bd228dede2165be71e5c9dcf76cc
......@@ -135,7 +137,8 @@ function test_package_postprocces()
local pack_nomod = {
Package = "wget",
Version = "1.17.1-1",
Architecture = "mpc85xx"
Architecture = "mpc85xx",
ChangedFiles = {},
}
local pack_nomod_cp = {}
for n, v in pairs(pack_nomod) do
......@@ -160,6 +163,7 @@ function test_status_parse()
Package = "kmod-usb-storage",
Version = "3.18.21+10-1-70ea6b9a4b789c558ac9d579b5c1022f-10",
Architecture = "mpc85xx",
ChangedFiles = {},
Source = "package/kernel/linux",
License = "GPLv2",
Section = "kernel",
......@@ -178,6 +182,7 @@ function test_status_parse()
Package = "terminfo",
Version = "5.9-2",
Architecture = "mpc85xx",
ChangedFiles = {},
Source = "package/libs/ncurses",
License = "MIT",
LicenseFiles = "README",
......@@ -205,6 +210,7 @@ function test_status_parse()
Package = "dnsmasq-dhcpv6",
Version = "2.73-1",
Architecture = "mpc85xx",
ChangedFiles = {},
Source = "package/network/services/dnsmasq",
License = "GPL-2.0",
LicenseFiles = "COPYING",
......@@ -233,6 +239,7 @@ function test_status_parse()
Package = "ucollect-count",
Version = "27",
Architecture = "mpc85xx",
ChangedFiles = {},
["Installed-Time"] = "1453896279",
Depends = "libc, ucollect-prog",
Status = std_status,
......@@ -773,6 +780,7 @@ function test_status_parse_dump()
status["New"] = {
Package = "New",
Version = "1",
ChangedFiles = {},
["Installed-Time"] = "1",
Depends = "Dep1, dep2",
Status = { "flag" }
......@@ -973,4 +981,4 @@ function teardown()
B.root_dir= orig_root_dir
utils.cleanup_dirs(tmp_dirs)
tmp_dirs = {}
end
end
......@@ -11,7 +11,7 @@ globals = {
-- lua51 doesn't contains?
"_M",
-- From interpreter.c
"log", "state_log_enabled", "state_dump", "run_command", "run_util", "download", "events_wait", "mkdtemp", "chdir", "getcwd", "mkdir", "move", "ls", "stat", "lstat", "sync", "setenv", "md5", "sha256", "md5_file", "sha256_file", "reexec", "uri_internal_get", "system_reboot", "get_updater_version",
"log", "state_log_enabled", "state_dump", "run_command", "run_util", "download", "events_wait", "mkdtemp", "chdir", "getcwd", "mkdir", "move", "copy", "ls", "stat", "lstat", "sync", "setenv", "md5", "sha256", "md5_file", "sha256_file", "reexec", "uri_internal_get", "system_reboot", "get_updater_version",
-- From logging
"ERROR", "WARN", "INFO", "DBG", "TRACE", "DIE", "log_event", "c_pcall_error_handler",
-- From dumper
......
......@@ -5,6 +5,6 @@ for file in "$ROOT_DIR"/usr/lib/opkg/info/*.list; do
LC_ALL=C sort -d -o "$file".out "$file"
mv -f "$file".out "$file"
done
for file in "$ROOT_DIR"/var/opkg-collided/"$ROOT_DIR"/*; do
mv "$file" "$ROOT_DIR"/var/opkg-collided/
for file in "$ROOT_DIR"/usr/share/updater/collided/"$ROOT_DIR"/*; do
mv "$file" "$ROOT_DIR"/usr/share/updater/collided
done
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