1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
|
%%
%% 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.
|