summaryrefslogtreecommitdiff
path: root/src/perm.erl
diff options
context:
space:
mode:
authorMagnus Ahltorp <map@kth.se>2014-09-25 01:35:33 +0200
committerMagnus Ahltorp <map@kth.se>2014-09-25 17:52:50 +0200
commit29ac49eabca61c4a9e0c3a0d8f9ba57ab516ebae (patch)
tree387b2353d82b96a32e3a5aeef669b2f8ec684217 /src/perm.erl
parenteb9b05d4b9213c9481a7005c63fa9107fe1b879f (diff)
Permanent storage implementation
Diffstat (limited to 'src/perm.erl')
-rw-r--r--src/perm.erl95
1 files changed, 95 insertions, 0 deletions
diff --git a/src/perm.erl b/src/perm.erl
new file mode 100644
index 0000000..2ce5b46
--- /dev/null
+++ b/src/perm.erl
@@ -0,0 +1,95 @@
+%%
+%% 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.