X-Git-Url: https://git.enpas.org/?a=blobdiff_plain;f=util%2Fdatamanager.lua;h=83f3dd13042d7607c8d5c2144a18f7512b4ec641;hb=6d501b1f0624c4829a787898181bf8a47dc47b35;hp=4a4d62b3c098519f361912162f9a73e30209c960;hpb=5a1000e8e61beb0c9ecf56ad9342d5b7031f9b49;p=prosody.git diff --git a/util/datamanager.lua b/util/datamanager.lua index 4a4d62b3..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 @@ -209,33 +209,62 @@ function store(username, host, datastore, data) return true; end -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 +-- 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 - log("error", "Unable to write to %s storage ('%s') for user: %s@%s", datastore, msg, username or "nil", host or "nil"); - return; + -- 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 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 + ok, msg = fallocate(f, pos, #data); + if not ok then + log("warn", "fallocate() failed: %s", tostring(msg)); + -- This doesn't work on every file system + end + + if f:seek() ~= pos then + log("debug", "fallocate() changed file position"); + f:seek("set", pos); + end + + 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 + +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 + + 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 @@ -259,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 @@ -287,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) @@ -306,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)); @@ -346,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)); @@ -362,6 +395,20 @@ function purge(username, host) return #errs == 0, t_concat(errs, ", "); end -_M.path_decode = decode; -_M.path_encode = encode; -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; +};