Verified Commit 3d0d1f63 authored by Karel Koci's avatar Karel Koci 🤘

Automatically try to recover journal if pkgupdate is executed

parent a4234876
......@@ -54,7 +54,6 @@ local DBG = DBG
local WARN = WARN
local DataDumper = DataDumper
local utils = require "utils"
local journal = require "journal"
local locks = require "locks"
module "backend"
......@@ -104,7 +103,6 @@ function root_dir_set(dir)
pkg_temp_dir = dir .. pkg_temp_dir_suffix
flags_storage = dir .. flags_storage_suffix
dir_opkg_collided = dir .. dir_opkg_collided_suffix
journal.path = dir .. "/usr/share/updater/journal"
end
--[[
......
......@@ -21,6 +21,7 @@
#include "util.h"
#include "inject.h"
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
#include <sys/types.h>
......@@ -123,11 +124,17 @@ static bool journal_open(lua_State *L, int flags) {
DBG("Opening journal");
if (fd != -1)
luaL_error(L, "Journal already open");
lua_getglobal(L, "journal");
lua_getfield(L, -1, "path");
const char *path = lua_tostring(L, -1);
fd = open(path, O_RDWR | O_DSYNC | O_APPEND | flags, S_IRUSR | S_IWUSR);
// Get current root directory
// TODO this should probably be argument instead
lua_getglobal(L, "backend");
lua_getfield(L, -1, "root_dir");
const char *root_dir = lua_tostring(L, -1);
journal_path = malloc(strlen(root_dir) + strlen(DEFAULT_JOURNAL_PATH) + 1);
strcpy(journal_path, root_dir);
strcat(journal_path, DEFAULT_JOURNAL_PATH);
fd = open(journal_path, O_RDWR | O_DSYNC | O_APPEND | flags, S_IRUSR | S_IWUSR);
if (fd == -1) {
free(journal_path);
switch (errno) {
case EEXIST:
luaL_error(L, "Unfinished journal exists");
......@@ -140,8 +147,6 @@ static bool journal_open(lua_State *L, int flags) {
}
}
ASSERT_MSG(fcntl(fd, F_SETFD, (long)FD_CLOEXEC) != -1, "Failed to set close on exec on journal FD: %s", strerror(errno));
// Keep a copy of the journal path, someone might change it and we want to remove the correct journal on finish
journal_path = strdup(path);
return true;
}
......@@ -329,8 +334,6 @@ void journal_mod_init(lua_State *L) {
TRACE("Journal module init");
// Create _M
lua_newtable(L);
// Some variables
inject_str_const(L, "journal", "path", DEFAULT_JOURNAL_PATH);
// journal.XXX = int(XXX) - init the constants
#define X(VAL) TRACE("Injecting constant journal." #VAL); lua_pushinteger(L, RT_##VAL); lua_setfield(L, -2, #VAL);
RECORD_TYPES
......@@ -338,3 +341,12 @@ void journal_mod_init(lua_State *L) {
inject_func_n(L, "journal", inject, sizeof inject / sizeof *inject);
inject_module(L, "journal");
}
bool journal_exists(const char *root_dir) {
if (fd != -1)
return true; // journal already open so it exists
char *path = alloca(strlen(root_dir) + strlen(DEFAULT_JOURNAL_PATH) + 1);
strcpy(path, root_dir);
strcat(path, DEFAULT_JOURNAL_PATH);
return access(path, F_OK) == 0;
}
......@@ -21,8 +21,12 @@
#define UPDATER_JOURNAL_H
#include <lua.h>
#include <stdbool.h>
// Create the journal module and inject it into the lua state
void journal_mod_init(lua_State *lua) __attribute__((nonnull));
// Check if there is some journal (this function won't open it)
bool journal_exists(const char *root_dir);
#endif
......@@ -7,5 +7,6 @@ pkgupdate_MODULES := \
uriinternal.embed
pkgupdate_LOCAL_LIBS := updater
pkgupdate_PKG_CONFIGS := $(LUA_NAME)
DOCS += src/pkgupdate/interaction
......@@ -21,6 +21,7 @@
#include "../lib/interpreter.h"
#include "../lib/util.h"
#include "../lib/arguments.h"
#include "../lib/journal.h"
#include <stdlib.h>
#include <stdbool.h>
......@@ -222,10 +223,18 @@ int main(int argc, char *argv[]) {
goto CLEANUP;
if (early_exit)
goto CLEANUP;
size_t result_count;
// Check if we should recover previous execution first if so do
if (journal_exists(root_dir)) {
INFO("Detected existing journal. Trying to recover it.");
const char *err = interpreter_call(interpreter, "transaction.recover_pretty", &result_count, "");
ASSERT_MSG(!err, "%s", err);
if (!results_interpret(interpreter, result_count))
goto CLEANUP;
}
// Decide what packages need to be downloaded and handled
const char *err = interpreter_call(interpreter, "updater.prepare", NULL, "s", top_level_config);
ASSERT_MSG(!err, "%s", err);
size_t result_count;
err = interpreter_call(interpreter, "transaction.empty", &result_count, "");
ASSERT_MSG(!err, "%s", err);
ASSERT_MSG(result_count == 1, "Wrong number of results of transaction.empty");
......
......@@ -893,7 +893,6 @@ function test_root_dir_set()
assert_equal("/dir/usr/lib/opkg/status", B.status_file)
assert_equal("/dir/usr/lib/opkg/info/", B.info_dir)
assert_equal("/dir/usr/share/updater/unpacked", B.pkg_temp_dir)
assert_equal("/dir/usr/share/updater/journal", journal.path)
end
function test_config_modified()
......
......@@ -21,9 +21,13 @@ along with Updater. If not, see <http://www.gnu.org/licenses/>.
require 'lunit'
require 'utils'
local J = require 'journal'
local backend = require 'backend'
module("journal-tests", package.seeall, lunit.testcase)
-- This is directory where journal directory should be created
journal_path = "/usr/share/updater"
local tmp_dirs = {}
-- Test the module is injected correctly
......@@ -33,7 +37,6 @@ end
-- Test values of some variables and "constants" ‒ make sure they are there
function test_values()
assert_string(J.path)
local types = {"START", "FINISH", "UNPACKED", "CHECKED", "MOVED", "SCRIPTS", "CLEANED"}
for i, t in ipairs(types) do
assert_number(J[t])
......@@ -49,8 +52,13 @@ end
function dir_init()
local dir = mkdtemp()
table.insert(tmp_dirs, dir)
J.path = dir .. "/journal"
return dir
backend.root_dir = dir
mkpath = dir
for dr in journal_path:gmatch('[^/]+') do
mkpath = mkpath .. '/' .. dr
mkdir(mkpath)
end
return dir .. journal_path
end
-- Check creating a fresh journal
......@@ -67,7 +75,7 @@ function test_fresh()
assert_false(J.opened())
assert_table_equal({}, ls(dir))
-- Create a fake journal file
io.open(J.path, "w"):close()
io.open(dir .. '/journal', "w"):close()
assert_table_equal({journal = "r"}, ls(dir));
-- Can't open fresh journal, if there's one already
assert_error(function () J.fresh() end)
......@@ -112,13 +120,13 @@ end
-- The journal is incomplete, test it can read the complete part
function test_recover_broken()
dir_init()
dir = dir_init()
J.fresh()
J.write(J.UNPACKED, { data = "xyz" }, { "x", "y", "z" })
J.finish(true)
-- Now damage the file a little bit
local content = utils.slurp(J.path)
local f, err = io.open(J.path, "w")
local content = utils.slurp(dir .. '/journal')
local f, err = io.open(dir .. '/journal', "w")
assert(f, err)
-- Store everything except for the last 3 bytes. That should kill the last FINISH record
f:write(content:sub(1, -3))
......
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