%%% Copyright (c) 2014, NORDUnet A/S. %%% See LICENSE for licensing information. -module(perm). -export([ensurefile/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) -> lager:debug("dir ~p key ~p", [Rootdir, Key]), {Dirs, Path} = path_for_key(Rootdir, Key), case readfile_and_verify(Path, Content) of ok -> lager:debug("key ~p existed, fsync", [Key]), util:fsync([Path, Rootdir | Dirs]), lager:debug("key ~p fsynced", [Key]); differ -> lager:debug("key ~p existed, was different", [Key]), differ; {error, enoent} -> lager:debug("key ~p didn't exist, add", [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), lager:debug("key ~p added, fsync", [Key]), util:fsync([Path, Rootdir | Dirs]), lager:debug("key ~p fsynced", [Key]); {error, Error} -> util:exit_with_error(Error, readfile, "Error reading file") end. -spec readfile(string(), binary()) -> binary(). readfile(Rootdir, Key) -> {_Dirs, Path} = path_for_key(Rootdir, Key), atomic:readfile(Path).