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"));
