summaryrefslogtreecommitdiff
path: root/src/perm.erl
blob: 5de82314037c55fae466bbabb1feb278601213c1 (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
96
97
%%% Copyright (c) 2014-2015, NORDUnet A/S.
%%% See LICENSE for licensing information.

-module(perm).
-export([ensurefile/3, ensurefile_nosync/3, readfile/2]).

-spec readfile_and_verify(string(), binary()) -> ok | differ | {error, atom()}.
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.

-spec make_dir(string()) -> ok | {error, atom()}.
make_dir(Name) ->
    case file:make_dir(Name) of
	ok ->
	    ok;
	{error, eexist} ->
	    ok;
	{error, Error} ->
	    {error, Error}
    end.

-spec make_dirs([string()]) -> ok | {error, atom()}.
make_dirs([]) ->
    ok;
make_dirs([Name | Rest]) ->
    case make_dir(Name) of
	ok ->
	    make_dirs(Rest);
	{error, Error} ->
	    {error, Error}
    end.

-spec path_for_key(string(), binary()) -> {[string()], string()}.
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}.

-spec ensurefile(string(), binary(), binary()) -> ok | differ.
ensurefile(Rootdir, Key, Content) ->
    ensurefile(Rootdir, Key, Content, sync).

-spec ensurefile_nosync(string(), binary(), binary()) -> ok | differ.
ensurefile_nosync(Rootdir, Key, Content) ->
    ensurefile(Rootdir, Key, Content, nosync).

ensurefile(Rootdir, Key, Content, Syncflag) ->
    lager:debug("dir ~p key ~s", [Rootdir, mochihex:to_hex(Key)]),
    {Dirs, Path} = path_for_key(Rootdir, Key),
    Result =
        case readfile_and_verify(Path, Content) of
            ok ->
                lager:debug("key ~s existed, fsync", [mochihex:to_hex(Key)]),
                ok = util:fsync([Path, Rootdir | Dirs]),
                lager:debug("key ~s fsynced", [mochihex:to_hex(Key)]),
                ok;
            differ ->
                lager:debug("key ~s existed, was different",
                            [mochihex:to_hex(Key)]),
                differ;
            {error, enoent} ->
                lager:debug("key ~s didn't exist, add", [mochihex:to_hex(Key)]),
                util:check_error(make_dirs([Rootdir, Rootdir ++ "nursery/"]
                                           ++ Dirs),
                                 makedir, "Error creating directory"),
                NurseryName = Rootdir ++ "nursery/" ++
                    util:tempfilename(hex:bin_to_hexstr(Key)),
                util:write_tempfile_and_rename(Path, NurseryName, Content),
                ok;
            {error, Error} ->
                util:exit_with_error(Error, readfile, "Error reading file")
        end,
    case Syncflag of
        sync ->
            lager:debug("key ~s added, fsync", [mochihex:to_hex(Key)]),
            ok = util:fsync([Path, Rootdir | Dirs]),
            lager:debug("key ~s fsynced", [mochihex:to_hex(Key)]),
            Result;
        nosync ->
            Result
    end.

-spec readfile(string(), binary()) -> binary() | noentry.
readfile(Rootdir, Key) ->
    {_Dirs, Path} = path_for_key(Rootdir, Key),
    atomic:readfile(Path).