diff options
author | Kim Alvefur <zash@zash.se> | 2012-07-29 03:26:03 +0200 |
---|---|---|
committer | Kim Alvefur <zash@zash.se> | 2012-07-29 03:26:03 +0200 |
commit | f69692e11dd4d737257d4cabc0861ec0d301123e (patch) | |
tree | f216b0fd8d6b35754d43d71612b78e353d97e2b7 /util | |
parent | 38ce1b2b86c0ce3d46051aec2ffadf02a740e0e0 (diff) | |
download | prosody-f69692e11dd4d737257d4cabc0861ec0d301123e.tar.gz prosody-f69692e11dd4d737257d4cabc0861ec0d301123e.zip |
util.datamanager: Write to a temporary file and atomically move it into place
Diffstat (limited to 'util')
-rw-r--r-- | util/datamanager.lua | 47 |
1 files changed, 33 insertions, 14 deletions
diff --git a/util/datamanager.lua b/util/datamanager.lua index 29a5b994..5d3074a3 100644 --- a/util/datamanager.lua +++ b/util/datamanager.lua @@ -15,11 +15,12 @@ local pcall = pcall; local log = require "util.logger".init("datamanager"); local io_open = io.open; local os_remove = os.remove; +local os_rename = os.rename; local tostring, tonumber = tostring, tonumber; local error = error; local next = next; local t_insert = table.insert; -local append = require "util.serialization".append; +local t_concat = table.concat; local envloadfile = require"util.envload".envloadfile; local serialize = require "util.serialization".serialize; local path_separator = assert ( package.config:match ( "^([^\n]+)" ) , "package.config not in standard form" ) -- Extract directory seperator from package.config (an undocumented string that comes with lua) @@ -149,6 +150,28 @@ function load(username, host, datastore) return ret; end +local function atomic_store(filename, data) + local scratch = filename.."~"; + local f, ok, msg; + repeat + f, msg = io_open(scratch, "w"); + if not f then break end + + ok, msg = f:write(data); + if not ok then break end + + ok, msg = f:close(); + if not ok then break end + + return os_rename(scratch, filename); + until false; + + -- Cleanup + if f then f:close(); end + os_remove(scratch); + return nil, msg; +end + function store(username, host, datastore, data) if not data then data = {}; @@ -160,14 +183,12 @@ function store(username, host, datastore, data) end -- save the datastore - local f, msg = io_open(getpath(username, host, datastore, nil, true), "w+"); - if not f then + local d = "return " .. serialize(data) .. ";\n"; + local ok, msg = atomic_store(getpath(username, host, datastore, nil, true), d); + if not ok then log("error", "Unable to write to %s storage ('%s') for user: %s@%s", datastore, msg, username or "nil", host or "nil"); return nil, "Error saving to storage"; end - f:write("return "); - append(f, data); - f:close(); if next(data) == nil then -- try to delete empty datastore log("debug", "Removing empty %s datastore for user %s@%s", datastore, username or "nil", host or "nil"); os_remove(getpath(username, host, datastore)); @@ -206,17 +227,15 @@ function list_store(username, host, datastore, data) end if callback(username, host, datastore) == false then return true; end -- save the datastore - local f, msg = io_open(getpath(username, host, datastore, "list", true), "w+"); - if not f then + local d = {}; + for _, item in ipairs(data) do + d[#d+1] = "item(" .. serialize(item) .. ");\n"; + end + local ok, msg = atomic_store(getpath(username, host, datastore, "list", true), t_concat(d)); + if not ok then log("error", "Unable to write to %s storage ('%s') for user: %s@%s", datastore, msg, username or "nil", host or "nil"); return; end - for _, d in ipairs(data) do - f:write("item("); - append(f, d); - f:write(");\n"); - end - f:close(); if next(data) == nil then -- try to delete empty datastore log("debug", "Removing empty %s datastore for user %s@%s", datastore, username or "nil", host or "nil"); os_remove(getpath(username, host, datastore, "list")); |