summaryrefslogtreecommitdiff
path: root/src/perm.erl
blob: 2ce5b46a6ba31dee1feca997f38bdb5a9769d2f6 (plain)
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.