X-Git-Url: https://git.enpas.org/?a=blobdiff_plain;f=util%2Fdatamanager.lua;h=08d9d6af2e40541aeab30e97b52f92d4af0872fb;hb=a0dc04ad05a5a436c967383e9ba06d5cc11e2e88;hp=5d3074a322883774e6e66f24df7b6bf043975be2;hpb=9fe01c4331cadb424aa1cf6222b61810c97752ed;p=prosody.git diff --git a/util/datamanager.lua b/util/datamanager.lua index 5d3074a3..08d9d6af 100644 --- a/util/datamanager.lua +++ b/util/datamanager.lua @@ -8,16 +8,15 @@ local format = string.format; -local setmetatable, type = setmetatable, type; -local pairs, ipairs = pairs, ipairs; +local setmetatable = setmetatable; +local ipairs = ipairs; local char = string.char; 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 tonumber = tonumber; local next = next; local t_insert = table.insert; local t_concat = table.concat; @@ -26,28 +25,23 @@ 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) local lfs = require "lfs"; local prosody = prosody; -local raw_mkdir; -local fallocate; - -if prosody.platform == "posix" then - raw_mkdir = require "util.pposix".mkdir; -- Doesn't trample on umask - fallocate = require "util.pposix".fallocate; -else - raw_mkdir = lfs.mkdir; -end -if not fallocate then -- Fallback - function fallocate(f, offset, len) - -- This assumes that current position == offset - local fake_data = (" "):rep(len); - local ok, msg = f:write(fake_data); - if not ok then - return ok, msg; - end - f:seek(offset); - return true; +local raw_mkdir = lfs.mkdir; +local function fallocate(f, offset, len) + -- This assumes that current position == offset + local fake_data = (" "):rep(len); + local ok, msg = f:write(fake_data); + if not ok then + return ok, msg; end -end + f:seek("set", offset); + return true; +end; +pcall(function() + local pposix = require "util.pposix"; + raw_mkdir = pposix.mkdir or raw_mkdir; -- Doesn't trample on umask + fallocate = pposix.fallocate or fallocate; +end); module "datamanager" @@ -172,6 +166,18 @@ local function atomic_store(filename, data) return nil, msg; end +if 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) + local f, err = io_open(filename, "w"); + if not f then return f, err; end + local ok, msg = f:write(data); + if not ok then f:close(); return ok, msg; end + return f:close(); + end +end + function store(username, host, datastore, data) if not data then data = {}; @@ -202,7 +208,10 @@ 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), "a"); + 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; @@ -268,45 +277,85 @@ function list_load(username, host, datastore) return items; end -function list_stores(username, host) - if not host then - return nil, "bad argument #2 to 'list_stores' (string expected, got nothing)"; +local type_map = { + keyval = "dat"; + list = "list"; +} + +function users(host, store, typ) + typ = type_map[typ or "keyval"]; + local store_dir = format("%s/%s/%s", data_path, encode(host), encode(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 end - local list = {}; - local host_dir = format("%s/%s/", data_path, encode(host)); - for node in lfs.dir(host_dir) do - if not node:match"^%." then -- dots should be encoded, this is probably . or .. - local store = decode(node); - local path = host_dir..node; - if username == true then - if lfs.attributes(path, "mode") == "directory" then - list[#list+1] = store; - end - elseif username then - if lfs.attributes(getpath(username, host, store), "mode") - or lfs.attributes(getpath(username, host, store, "list"), "mode") then - list[#list+1] = store; + local next, state = lfs.dir(store_dir); + return function(state) + for node in next, state do + local file, ext = node:match("^(.*)%.([dalist]+)$"); + if file and ext == typ then + return decode(file); + end + end + end, state; +end + +function stores(username, host, typ) + typ = type_map[typ or "keyval"]; + local store_dir = format("%s/%s/", data_path, encode(host)); + + local mode, err = lfs.attributes(store_dir, "mode"); + if not mode then + return function() log("debug", err or (store_dir .. " does not exist")) end + end + local next, state = lfs.dir(store_dir); + return function(state) + for node in next, state do + if not node:match"^%." then + if username == true then + if lfs.attributes(store_dir..node, "mode") == "directory" then + return decode(node); + end + elseif username then + local store = decode(node) + if lfs.attributes(getpath(username, host, store, typ), "mode") then + return store; + end + elseif lfs.attributes(node, "mode") == "file" then + local file, ext = node:match("^(.*)%.([dalist]+)$"); + if ext == typ then + return decode(file) + end end - elseif lfs.attributes(path, "mode") == "file" then - list[#list+1] = store:gsub("%.[dalist]+$",""); end end + end, state; +end + +local function do_remove(path) + local ok, err = os_remove(path); + if not ok and lfs.attributes(path, "mode") then + return ok, err; end - return list; + return true end function purge(username, host) local host_dir = format("%s/%s/", data_path, encode(host)); local deleted = 0; + local errs = {}; for file in lfs.dir(host_dir) do if lfs.attributes(host_dir..file, "mode") == "directory" then local store = decode(file); - deleted = deleted + (os_remove(getpath(username, host, store)) and 1 or 0); - deleted = deleted + (os_remove(getpath(username, host, store, "list")) and 1 or 0); - -- We this will generate loads of "No such file or directory", but do we care? + local ok, err = do_remove(getpath(username, host, store)); + if not ok then errs[#errs+1] = err; end + + local ok, err = do_remove(getpath(username, host, store, "list")); + if not ok then errs[#errs+1] = err; end end end - return deleted > 0, deleted; + return #errs == 0, t_concat(errs, ", "); end return _M;