trunk

changeset 5049:5d685f123332

util.datamanager: Write to a temporary file and atomically move it into place
author Kim Alvefur <zash@zash.se>
date Sun, 29 Jul 2012 03:26:03 +0200
parents e02161ba20e0
children 2ad43c183d00
files util/datamanager.lua
diffstat 1 files changed, 33 insertions(+), 14 deletions(-) [+]
line diff
     1.1 --- a/util/datamanager.lua	Sun Jul 29 01:37:15 2012 +0200
     1.2 +++ b/util/datamanager.lua	Sun Jul 29 03:26:03 2012 +0200
     1.3 @@ -15,11 +15,12 @@
     1.4  local log = require "util.logger".init("datamanager");
     1.5  local io_open = io.open;
     1.6  local os_remove = os.remove;
     1.7 +local os_rename = os.rename;
     1.8  local tostring, tonumber = tostring, tonumber;
     1.9  local error = error;
    1.10  local next = next;
    1.11  local t_insert = table.insert;
    1.12 -local append = require "util.serialization".append;
    1.13 +local t_concat = table.concat;
    1.14  local envloadfile = require"util.envload".envloadfile;
    1.15  local serialize = require "util.serialization".serialize;
    1.16  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)
    1.17 @@ -149,6 +150,28 @@
    1.18  	return ret;
    1.19  end
    1.20  
    1.21 +local function atomic_store(filename, data)
    1.22 +	local scratch = filename.."~";
    1.23 +	local f, ok, msg;
    1.24 +	repeat
    1.25 +		f, msg = io_open(scratch, "w");
    1.26 +		if not f then break end
    1.27 +
    1.28 +		ok, msg = f:write(data);
    1.29 +		if not ok then break end
    1.30 +
    1.31 +		ok, msg = f:close();
    1.32 +		if not ok then break end
    1.33 +
    1.34 +		return os_rename(scratch, filename);
    1.35 +	until false;
    1.36 +
    1.37 +	-- Cleanup
    1.38 +	if f then f:close(); end
    1.39 +	os_remove(scratch);
    1.40 +	return nil, msg;
    1.41 +end
    1.42 +
    1.43  function store(username, host, datastore, data)
    1.44  	if not data then
    1.45  		data = {};
    1.46 @@ -160,14 +183,12 @@
    1.47  	end
    1.48  
    1.49  	-- save the datastore
    1.50 -	local f, msg = io_open(getpath(username, host, datastore, nil, true), "w+");
    1.51 -	if not f then
    1.52 +	local d = "return " .. serialize(data) .. ";\n";
    1.53 +	local ok, msg = atomic_store(getpath(username, host, datastore, nil, true), d);
    1.54 +	if not ok then
    1.55  		log("error", "Unable to write to %s storage ('%s') for user: %s@%s", datastore, msg, username or "nil", host or "nil");
    1.56  		return nil, "Error saving to storage";
    1.57  	end
    1.58 -	f:write("return ");
    1.59 -	append(f, data);
    1.60 -	f:close();
    1.61  	if next(data) == nil then -- try to delete empty datastore
    1.62  		log("debug", "Removing empty %s datastore for user %s@%s", datastore, username or "nil", host or "nil");
    1.63  		os_remove(getpath(username, host, datastore));
    1.64 @@ -206,17 +227,15 @@
    1.65  	end
    1.66  	if callback(username, host, datastore) == false then return true; end
    1.67  	-- save the datastore
    1.68 -	local f, msg = io_open(getpath(username, host, datastore, "list", true), "w+");
    1.69 -	if not f then
    1.70 +	local d = {};
    1.71 +	for _, item in ipairs(data) do
    1.72 +		d[#d+1] = "item(" .. serialize(item) .. ");\n";
    1.73 +	end
    1.74 +	local ok, msg = atomic_store(getpath(username, host, datastore, "list", true), t_concat(d));
    1.75 +	if not ok then
    1.76  		log("error", "Unable to write to %s storage ('%s') for user: %s@%s", datastore, msg, username or "nil", host or "nil");
    1.77  		return;
    1.78  	end
    1.79 -	for _, d in ipairs(data) do
    1.80 -		f:write("item(");
    1.81 -		append(f, d);
    1.82 -		f:write(");\n");
    1.83 -	end
    1.84 -	f:close();
    1.85  	if next(data) == nil then -- try to delete empty datastore
    1.86  		log("debug", "Removing empty %s datastore for user %s@%s", datastore, username or "nil", host or "nil");
    1.87  		os_remove(getpath(username, host, datastore, "list"));