%% %% Copyright (c) 2014 Kungliga Tekniska Högskolan %% (KTH Royal Institute of Technology, Stockholm, Sweden). %% -module(perm). -export([ensurefile/3]). fsync(Name) -> fsyncport:fsync(Name). readfile_and_verify(Name, Content) -> case file:read_file(Name) of {ok, ContentsReadBinary} -> ContentsRead = binary_to_list(ContentsReadBinary), if Content == ContentsRead -> ok; true -> {error, "File contents differ"} end; {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. ensurefile(Rootdir, Key, Content) -> {Dirs, Path} = path_for_key(Rootdir, Key), case readfile_and_verify(Path, Content) of ok -> lists:foreach(fun (Dir) -> fsync(Dir) end, [Path, Rootdir | Dirs]); {error, enoent} -> case make_dirs([Rootdir, Rootdir ++ "nursery/"] ++ Dirs) of ok -> NurseryName = Rootdir ++ "nursery/" ++ tempfilename(hex:bin_to_hexstr(Key)), _Result = writefile(Path, NurseryName, Content), lists:foreach(fun (Dir) -> fsync(Dir) end, [Path, Rootdir | Dirs]); %% XXX check results {error, Error} -> io:format("Error creating directory: ~w~n", [Error]) end; {error, Error} -> exit({perm, fileerror, "Error reading file", Error}) end.