%%% Copyright (c) 2014-2015, NORDUnet A/S. %%% See LICENSE for licensing information. -module(perm). -export([ensurefile/3, ensurefile_nosync/3, readfile/2]). -spec readfile_and_verify(string(), binary()) -> ok | differ | {error, atom()}. readfile_and_verify(Name, Content) -> case file:read_file(Name) of {ok, ContentsRead} when Content == ContentsRead -> ok; {ok, _ContentsRead} -> differ; {error, Error} -> {error, Error} end. -spec make_dir(string()) -> ok | {error, atom()}. make_dir(Name) -> case file:make_dir(Name) of ok -> ok; {error, eexist} -> ok; {error, Error} -> {error, Error} end. -spec make_dirs([string()]) -> ok | {error, atom()}. make_dirs([]) -> ok; make_dirs([Name | Rest]) -> case make_dir(Name) of ok -> make_dirs(Rest); {error, Error} -> {error, Error} end. -spec path_for_key(string(), binary()) -> {[string()], string()}. path_for_key(Rootdir, Key) -> Name = hex:bin_to_hexstr(Key), [C1, C2, C3, C4, C5, C6 | _] = Name, Firstlevel = Rootdir ++ [C1, C2], Secondlevel = Firstlevel ++ "/" ++ [C3, C4], Thirdlevel = Secondlevel ++ "/" ++ [C5, C6], Fullpath = Thirdlevel ++ "/" ++ Name, {[Firstlevel, Secondlevel, Thirdlevel], Fullpath}. -spec ensurefile(string(), binary(), binary()) -> ok | differ. ensurefile(Rootdir, Key, Content) -> ensurefile(Rootdir, Key, Content, sync). -spec ensurefile_nosync(string(), binary(), binary()) -> ok | differ. ensurefile_nosync(Rootdir, Key, Content) -> ensurefile(Rootdir, Key, Content, nosync). ensurefile(Rootdir, Key, Content, Syncflag) -> lager:debug("dir ~p key ~s", [Rootdir, mochihex:to_hex(Key)]), {Dirs, Path} = path_for_key(Rootdir, Key), Result = case readfile_and_verify(Path, Content) of ok -> lager:debug("key ~s existed, fsync", [mochihex:to_hex(Key)]), ok = util:fsync([Path, Rootdir | Dirs]), lager:debug("key ~s fsynced", [mochihex:to_hex(Key)]), ok; differ -> lager:debug("key ~s existed, was different", [mochihex:to_hex(Key)]), differ; {error, enoent} -> lager:debug("key ~s didn't exist, add", [mochihex:to_hex(Key)]), util:check_error(make_dirs([Rootdir, Rootdir ++ "nursery/"] ++ Dirs), makedir, "Error creating directory"), NurseryName = Rootdir ++ "nursery/" ++ util:tempfilename(hex:bin_to_hexstr(Key)), util:write_tempfile_and_rename(Path, NurseryName, Content), ok; {error, Error} -> util:exit_with_error(Error, readfile, "Error reading file") end, case Syncflag of sync -> lager:debug("key ~s added, fsync", [mochihex:to_hex(Key)]), ok = util:fsync([Path, Rootdir | Dirs]), lager:debug("key ~s fsynced", [mochihex:to_hex(Key)]), Result; nosync -> Result end. -spec readfile(string(), binary()) -> binary() | noentry. readfile(Rootdir, Key) -> {_Dirs, Path} = path_for_key(Rootdir, Key), atomic:readfile(Path).