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
|
%%
%% 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.
|