X-Git-Url: https://git.enpas.org/?a=blobdiff_plain;f=util%2Fdatamanager.lua;h=83f3dd13042d7607c8d5c2144a18f7512b4ec641;hb=6d501b1f0624c4829a787898181bf8a47dc47b35;hp=383e738fa1d93c1b104e284cf9a044d5ef0f6fcc;hpb=06f6e42b5a278638daaa22d007dac1733ec3901f;p=prosody.git diff --git a/util/datamanager.lua b/util/datamanager.lua index 383e738f..83f3dd13 100644 --- a/util/datamanager.lua +++ b/util/datamanager.lua @@ -43,7 +43,7 @@ pcall(function() fallocate = pposix.fallocate or fallocate; end); -module "datamanager" +local _ENV = nil; ---- utils ----- local encode, decode; @@ -74,7 +74,7 @@ local callbacks = {}; ------- API ------------- -function set_data_path(path) +local function set_data_path(path) log("debug", "Setting data path to: %s", path); data_path = path; end @@ -87,14 +87,14 @@ local function callback(username, host, datastore, data) return username, host, datastore, data; end -function add_callback(func) +local function add_callback(func) if not callbacks[func] then -- Would you really want to set the same callback more than once? callbacks[func] = true; callbacks[#callbacks+1] = func; return true; end end -function remove_callback(func) +local function remove_callback(func) if callbacks[func] then for i, f in ipairs(callbacks) do if f == func then @@ -106,7 +106,7 @@ function remove_callback(func) end end -function getpath(username, host, datastore, ext, create) +local function getpath(username, host, datastore, ext, create) ext = ext or "dat"; host = (host and encode(host)) or "_global"; username = username and encode(username); @@ -119,7 +119,7 @@ function getpath(username, host, datastore, ext, create) end end -function load(username, host, datastore) +local function load(username, host, datastore) local data, ret = envloadfile(getpath(username, host, datastore), {}); if not data then local mode = lfs.attributes(getpath(username, host, datastore), "mode"); @@ -163,7 +163,7 @@ local function atomic_store(filename, data) return nil, msg; end -if prosody.platform ~= "posix" then +if prosody and prosody.platform ~= "posix" then -- os.rename does not overwrite existing files on Windows -- TODO We could use Transactional NTFS on Vista and above function atomic_store(filename, data) @@ -175,7 +175,7 @@ if prosody.platform ~= "posix" then end end -function store(username, host, datastore, data) +local function store(username, host, datastore, data) if not data then data = {}; end @@ -187,47 +187,84 @@ function store(username, host, datastore, data) -- save the datastore local d = "return " .. serialize(data) .. ";\n"; - local ok, msg = atomic_store(getpath(username, host, datastore, nil, true), d); + local mkdir_cache_cleared; + repeat + local ok, msg = atomic_store(getpath(username, host, datastore, nil, true), d); + if not ok then + if not mkdir_cache_cleared then -- We may need to recreate a removed directory + _mkdir = {}; + mkdir_cache_cleared = true; + else + 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 + end + 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)); + end + -- we write data even when we are deleting because lua doesn't have a + -- platform independent way of checking for non-exisitng files + until ok; + return true; +end + +-- Append a blob of data to a file +local function append(username, host, datastore, ext, data) + if type(data) ~= "string" then return; end + local filename = getpath(username, host, datastore, ext, true); + + local ok; + local f, msg = io_open(filename, "r+"); + if not f then + -- File did probably not exist, let's create it + f, msg = io_open(filename, "w"); + if not f then + return nil, msg, "open"; + end + end + + local pos = f:seek("end"); + ok, msg = fallocate(f, pos, #data); 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"; + log("warn", "fallocate() failed: %s", tostring(msg)); + -- This doesn't work on every file system end - 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)); + + if f:seek() ~= pos then + log("debug", "fallocate() changed file position"); + f:seek("set", pos); end - -- we write data even when we are deleting because lua doesn't have a - -- platform independent way of checking for non-exisitng files - return true; + + ok, msg = f:write(data); + if not ok then + f:close(); + return ok, msg, "write"; + end + + ok, msg = f:close(); + if not ok then + return ok, msg; + end + + return true, pos; end -function list_append(username, host, datastore, data) +local function list_append(username, host, datastore, data) if not data then return; 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), "r+"); - if not f then - f, msg = io_open(getpath(username, host, datastore, "list", true), "w"); - end - if not f then - log("error", "Unable to write to %s storage ('%s') for user: %s@%s", datastore, msg, username or "nil", host or "nil"); - return; - end - local data = "item(" .. serialize(data) .. ");\n"; - local pos = f:seek("end"); - local ok, msg = fallocate(f, pos, #data); - f:seek("set", pos); - if ok then - f:write(data); - else + + data = "item(" .. serialize(data) .. ");\n"; + local ok, msg = append(username, host, datastore, "list", data); + 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 ok, msg; end - f:close(); return true; end -function list_store(username, host, datastore, data) +local function list_store(username, host, datastore, data) if not data then data = {}; end @@ -251,7 +288,7 @@ function list_store(username, host, datastore, data) return true; end -function list_load(username, host, datastore) +local function list_load(username, host, datastore) local items = {}; local data, ret = envloadfile(getpath(username, host, datastore, "list"), {item = function(i) t_insert(items, i); end}); if not data then @@ -279,13 +316,13 @@ local type_map = { list = "list"; } -function users(host, store, typ) +local function users(host, store, typ) typ = type_map[typ or "keyval"]; local store_dir = format("%s/%s/%s", data_path, encode(host), store); local mode, err = lfs.attributes(store_dir, "mode"); if not mode then - return function() log("debug", err or (store_dir .. " does not exist")) end + return function() log("debug", "%s", err or (store_dir .. " does not exist")) end end local next, state = lfs.dir(store_dir); return function(state) @@ -298,7 +335,7 @@ function users(host, store, typ) end, state; end -function stores(username, host, typ) +local function stores(username, host, typ) typ = type_map[typ or "keyval"]; local store_dir = format("%s/%s/", data_path, encode(host)); @@ -338,10 +375,14 @@ local function do_remove(path) return true end -function purge(username, host) +local function purge(username, host) local host_dir = format("%s/%s/", data_path, encode(host)); + local ok, iter, state, var = pcall(lfs.dir, host_dir); + if not ok then + return ok, iter; + end local errs = {}; - for file in lfs.dir(host_dir) do + for file in iter, state, var do if lfs.attributes(host_dir..file, "mode") == "directory" then local store = decode(file); local ok, err = do_remove(getpath(username, host, store)); @@ -354,4 +395,20 @@ function purge(username, host) return #errs == 0, t_concat(errs, ", "); end -return _M; +return { + set_data_path = set_data_path; + add_callback = add_callback; + remove_callback = remove_callback; + getpath = getpath; + load = load; + store = store; + append_raw = append; + list_append = list_append; + list_store = list_store; + list_load = list_load; + users = users; + stores = stores; + purge = purge; + path_decode = decode; + path_encode = encode; +};