%% %% Copyright (c) 2014 Kungliga Tekniska Högskolan %% (KTH Royal Institute of Technology, Stockholm, Sweden). %% -module(perm). -export([ensurefile/3, readfile/2]). fsync([]) -> ok; fsync([Name | Rest]) -> case fsyncport:fsync(Name) of ok -> fsync(Rest); {error, Error} -> {error, Error} end. 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. writefile(Name, NurseryName, Content) -> case file:open(NurseryName, [write, exclusive]) of {ok, File} -> %io:format("Write file: ~p~n", [Name]), ok = file:write(File, Content), file:close(File), Result = file:rename(NurseryName, Name), Result; {error, eexist} -> %% Should not happen, file name should be unique {error, eexist}; {error, Error} -> {error, Error} end. make_dir(Name) -> case file:make_dir(Name) of ok -> ok; {error, eexist} -> ok; {error, Error} -> {error, Error} end. make_dirs([]) -> ok; make_dirs([Name | Rest]) -> case make_dir(Name) of ok -> make_dirs(Rest); {error, Error} -> {error, Error} end. 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}. tempfilename(Base) -> {MegaSecs, Secs, MicroSecs} = now(), Filename = io_lib:format("~s-~s-~p.~p", [Base, os:getpid(), MegaSecs * 1000000 + Secs, MicroSecs]), Filename. exit_with_error(Error, Message) -> io:format("~s: ~w~n", [Message, Error]), exit({perm, fileerror, Message, Error}). check_error(ReturnValue, ErrorMessage) -> case ReturnValue of ok -> ok; {error, Error} -> exit_with_error(Error, ErrorMessage) end. ensurefile(Rootdir, Key, Content) -> {Dirs, Path} = path_for_key(Rootdir, Key), case readfile_and_verify(Path, Content) of ok -> check_error(fsync([Path, Rootdir | Dirs]), "Error in fsync"); differ -> differ; {error, enoent} -> check_error(make_dirs([Rootdir, Rootdir ++ "nursery/"] ++ Dirs), "Error creating directory"), NurseryName = Rootdir ++ "nursery/" ++ tempfilename(hex:bin_to_hexstr(Key)), _Result = writefile(Path, NurseryName, Content), check_error(fsync([Path, Rootdir | Dirs]), "Error in fsync"); {error, Error} -> exit_with_error(Error, "Error reading file") end. readfile(Rootdir, Key) -> {_Dirs, Path} = path_for_key(Rootdir, Key), case file:read_file(Path) of {ok, Contents} -> Contents; {error, enoent} -> noentry; {error, Error} -> exit_with_error(Error, "Error reading file") end.