1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 1997-2016. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20-module(ram_file).
21
22%% Binary RAM file interface
23
24%% Generic file contents operations
25-export([open/2, close/1]).
26-export([write/2, read/2, copy/3,
27	 pread/2, pread/3, pwrite/2, pwrite/3,
28	 position/2, truncate/1, datasync/1, sync/1]).
29
30%% Specialized file operations
31-export([get_size/1, get_file/1, advise/4]).
32-export([allocate/3]).
33-export([ipread_s32bu_p32bu/3]).
34
35%% Includes and defines
36-define(RAM_FILE_DRV, "ram_file_drv").
37-define(MAX_I32, (1 bsl 31)).
38-define(G_I32(X), is_integer(X), X >= -?MAX_I32, X < ?MAX_I32).
39
40-include("file.hrl").
41
42%% --------------------------------------------------------------------------
43%% These operation codes were once identical between efile_drv.c
44%% and ram_file_drv.c, but now these drivers are not depeding on each other.
45%% So, the codes could be changed to more logical values now, but why indeed?
46
47%% Defined "file" functions
48-define(RAM_FILE_OPEN,            1).
49-define(RAM_FILE_READ,            2).
50-define(RAM_FILE_LSEEK,           3).
51-define(RAM_FILE_WRITE,           4).
52-define(RAM_FILE_FSYNC,           9).
53-define(RAM_FILE_TRUNCATE,       14).
54-define(RAM_FILE_PREAD,          17).
55-define(RAM_FILE_PWRITE,         18).
56-define(RAM_FILE_FDATASYNC,      19).
57
58%% Other operations
59-define(RAM_FILE_GET,            30).
60-define(RAM_FILE_COMPRESS,       33).
61-define(RAM_FILE_UNCOMPRESS,     34).
62-define(RAM_FILE_SIZE,           37).
63-define(RAM_FILE_ADVISE,         38).
64-define(RAM_FILE_ALLOCATE,       39).
65
66%% Open modes for RAM_FILE_OPEN
67-define(RAM_FILE_MODE_READ,       1).
68-define(RAM_FILE_MODE_WRITE,      2).
69-define(RAM_FILE_MODE_READ_WRITE, 3).
70%% Use this mask to get just the mode bits to be passed to the driver.
71-define(RAM_FILE_MODE_MASK, 3).
72
73%% Seek modes for RAM_FILE_LSEEK
74-define(RAM_FILE_SEEK_SET,        0).
75-define(RAM_FILE_SEEK_CUR,        1).
76-define(RAM_FILE_SEEK_END,        2).
77
78%% Return codes
79-define(RAM_FILE_RESP_OK,         0).
80-define(RAM_FILE_RESP_ERROR,      1).
81-define(RAM_FILE_RESP_DATA,       2).
82-define(RAM_FILE_RESP_NUMBER,     3).
83-define(RAM_FILE_RESP_INFO,       4).
84
85%% POSIX file advises
86-define(POSIX_FADV_NORMAL,     0).
87-define(POSIX_FADV_RANDOM,     1).
88-define(POSIX_FADV_SEQUENTIAL, 2).
89-define(POSIX_FADV_WILLNEED,   3).
90-define(POSIX_FADV_DONTNEED,   4).
91-define(POSIX_FADV_NOREUSE,    5).
92
93%% --------------------------------------------------------------------------
94%% Generic file contents operations.
95%%
96%% Supposed to be called by applications through module file.
97
98open(Data, ModeList) when is_list(ModeList) ->
99    case open_mode(ModeList) of
100  	{Mode,Opts} when is_integer(Mode) ->
101  	    case ll_open(Data, Mode, Opts) of
102  		{ok,Port} ->
103  		    {ok,#file_descriptor{module=?MODULE, data=Port}};
104  		Error ->
105  		    Error
106 	    end;
107 	{error,_}=Error ->
108  	    Error
109    end.
110
111close(#file_descriptor{module = ?MODULE, data = Port}) ->
112    ll_close(Port).
113
114read(#file_descriptor{module = ?MODULE, data = Port}, Sz)
115  when is_integer(Sz), Sz >= 0 ->
116    if
117	?G_I32(Sz) ->
118	    Cmd = <<?RAM_FILE_READ:8,Sz:32>>,
119	    case call_port(Port, Cmd) of
120		{ok, {0, _Data}} when Sz =/= 0 ->
121		    eof;
122		{ok, {_Sz, Data}} ->
123		    {ok, Data};
124		{error, enomem} ->
125		    %% Garbage collecting here might help if
126		    %% the current processes has some old binaries left.
127		    erlang:garbage_collect(),
128		    case call_port(Port, Cmd) of
129			{ok, {0, _Data}} when Sz =/= 0 ->
130			    eof;
131			{ok, {_Sz, Data}} ->
132			    {ok, Data};
133			Error ->
134			    Error
135		    end;
136		Error ->
137		    Error
138	    end;
139	true ->
140	    {error, einval}
141    end.
142
143write(#file_descriptor{module = ?MODULE, data = Port}, Bytes) ->
144    case call_port(Port, [?RAM_FILE_WRITE | Bytes]) of
145	{ok, _Sz} ->
146	    ok;
147	Error ->
148	    Error
149    end.
150
151
152
153
154copy(#file_descriptor{module = ?MODULE} = Source,
155     #file_descriptor{module = ?MODULE} = Dest,
156     Length)
157  when is_integer(Length), Length >= 0;
158       is_atom(Length) ->
159    %% XXX Should be moved down to the driver for optimization.
160    file:copy_opened(Source, Dest, Length).
161
162datasync(#file_descriptor{module = ?MODULE, data = Port}) ->
163    call_port(Port, <<?RAM_FILE_FDATASYNC>>).
164
165sync(#file_descriptor{module = ?MODULE, data = Port}) ->
166    call_port(Port, <<?RAM_FILE_FSYNC>>).
167
168truncate(#file_descriptor{module = ?MODULE, data = Port}) ->
169    call_port(Port, <<?RAM_FILE_TRUNCATE>>).
170
171position(#file_descriptor{module = ?MODULE, data = Port}, Pos) ->
172    case lseek_position(Pos) of
173	{ok, Offs, Whence} when ?G_I32(Offs) ->
174	    call_port(Port, <<?RAM_FILE_LSEEK:8,Offs:32,Whence:32>>);
175	{ok, _, _} ->
176	    {error, einval};
177	Error ->
178	    Error
179    end.
180
181
182
183pread(#file_descriptor{module = ?MODULE, data = Port}, L) when is_list(L) ->
184    pread_1(Port, L, []).
185
186pread_1(Port, [], Cs) ->
187    pread_2(Port, lists:reverse(Cs), []);
188pread_1(Port, [{At, Sz} | T], Cs)
189  when is_integer(At), is_integer(Sz), Sz >= 0 ->
190    if
191	?G_I32(At), ?G_I32(Sz) ->
192	    pread_1(Port, T, [{Sz,<<?RAM_FILE_PREAD:8,At:32,Sz:32>>}|Cs]);
193	true ->
194	    {error, einval}
195    end;
196pread_1(_, _, _243) ->
197   {error, badarg}.
198
199pread_2(_Port, [], R) ->
200    {ok, lists:reverse(R)};
201pread_2(Port, [{Sz,Command}|Commands], R) ->
202    case call_port(Port, Command) of
203	{ok, {0,_Data}} when Sz =/= 0 ->
204	    pread_2(Port, Commands, [eof | R]);
205	{ok, {_Sz,Data}} ->
206	    pread_2(Port, Commands, [Data | R]);
207	Error ->
208	    Error
209    end.
210
211pread(#file_descriptor{module = ?MODULE, data = Port}, At, Sz)
212  when is_integer(At), is_integer(Sz), Sz >= 0 ->
213    if
214	?G_I32(At), ?G_I32(Sz) ->
215	    case call_port(Port, <<?RAM_FILE_PREAD:8,At:32,Sz:32>>) of
216		{ok, {0,_Data}} when Sz =/= 0 ->
217		    eof;
218		{ok, {_Sz,Data}} ->
219		    {ok, Data};
220		Error ->
221		    Error
222	    end;
223	true ->
224	    {error, einval}
225    end;
226pread(#file_descriptor{module = ?MODULE}, _, _) ->
227    {error, badarg}.
228
229
230
231pwrite(#file_descriptor{module = ?MODULE, data = Port}, L) when is_list(L) ->
232    pwrite_1(Port, L, 0, []).
233
234pwrite_1(Port, [], _, Cs) ->
235    pwrite_2(Port, lists:reverse(Cs), 0);
236pwrite_1(Port, [{At, Bytes} | T], R, Cs) when is_integer(At) ->
237    if
238	?G_I32(At), is_binary(Bytes) ->
239	    pwrite_1(Port, T, R+1,
240		     [<<?RAM_FILE_PWRITE:8,At:32,Bytes/binary>> | Cs]);
241	?G_I32(At) ->
242	    try erlang:iolist_to_binary(Bytes) of
243		Bin ->
244		    pwrite_1(Port, T, R+1,
245			     [<<?RAM_FILE_PWRITE:8,At:32,Bin/binary>> | Cs])
246	    catch
247		error:Reason ->
248		    {error, Reason}
249	    end;
250	true ->
251	    {error, {R, einval}}
252    end;
253pwrite_1(_, _, _, _) ->
254    {error, badarg}.
255
256pwrite_2(_Port, [], _R) ->
257    ok;
258pwrite_2(Port, [Command|Commands], R) ->
259    case call_port(Port, Command) of
260	{ok, _Sz} ->
261	    pwrite_2(Port, Commands, R+1);
262	{error, badarg} = Error ->
263	    Error;
264	{error, Reason} ->
265	    {error, {R, Reason}}
266    end.
267
268pwrite(#file_descriptor{module = ?MODULE, data = Port}, At, Bytes)
269  when is_integer(At) ->
270    if
271	?G_I32(At) ->
272	    case call_port(Port, [<<?RAM_FILE_PWRITE:8,At:32>>|Bytes]) of
273		{ok, _Sz} ->
274		    ok;
275		Error ->
276		    Error
277	    end;
278	true ->
279	    {error, einval}
280    end;
281pwrite(#file_descriptor{module = ?MODULE}, _, _) ->
282    {error, badarg}.
283
284
285ipread_s32bu_p32bu(#file_descriptor{module = ?MODULE} = Handle, Pos, MaxSz) ->
286    file:ipread_s32bu_p32bu_int(Handle, Pos, MaxSz).
287
288
289
290%% --------------------------------------------------------------------------
291%% Specialized ram_file API for functions not in file, unique to ram_file.
292%%
293
294
295get_file(#file_descriptor{module = ?MODULE, data = Port}) ->
296    case call_port(Port, [?RAM_FILE_GET]) of
297	{ok, {_Sz, Data}} ->
298	    {ok, Data};
299	Error ->
300	    Error
301    end;
302get_file(#file_descriptor{}) ->
303    {error, enotsup}.
304
305get_size(#file_descriptor{module = ?MODULE, data = Port}) ->
306    call_port(Port, [?RAM_FILE_SIZE]);
307get_size(#file_descriptor{}) ->
308    {error, enotsup}.
309
310advise(#file_descriptor{module = ?MODULE, data = Port}, Offset,
311        Length, Advise) ->
312    Cmd0 = <<?RAM_FILE_ADVISE, Offset:64/signed, Length:64/signed>>,
313    case Advise of
314    normal ->
315        call_port(Port, <<Cmd0/binary, ?POSIX_FADV_NORMAL:32/signed>>);
316    random ->
317        call_port(Port, <<Cmd0/binary, ?POSIX_FADV_RANDOM:32/signed>>);
318    sequential ->
319        call_port(Port, <<Cmd0/binary, ?POSIX_FADV_SEQUENTIAL:32/signed>>);
320    will_need ->
321        call_port(Port, <<Cmd0/binary, ?POSIX_FADV_WILLNEED:32/signed>>);
322    dont_need ->
323        call_port(Port, <<Cmd0/binary, ?POSIX_FADV_DONTNEED:32/signed>>);
324    no_reuse ->
325        call_port(Port, <<Cmd0/binary, ?POSIX_FADV_NOREUSE:32/signed>>);
326    _ ->
327        {error, einval}
328    end;
329advise(#file_descriptor{}, _Offset, _Length, _Advise) ->
330    {error, enotsup}.
331
332allocate(#file_descriptor{module = ?MODULE, data = Port}, Offset, Length) ->
333    call_port(Port, <<?RAM_FILE_ALLOCATE, Offset:64/signed, Length:64/signed>>);
334allocate(#file_descriptor{}, _Offset, _Length) ->
335    {error, enotsup}.
336
337
338
339%%%-----------------------------------------------------------------
340%%% Functions to communicate with the driver
341
342ll_open(Data, Mode, Opts) ->
343    try erlang:open_port({spawn, ?RAM_FILE_DRV}, Opts) of
344	Port ->
345	    case call_port(Port, [<<?RAM_FILE_OPEN:8,Mode:32>>|Data]) of
346		{error, _} = Error ->
347		    ll_close(Port),
348		    Error;
349		{ok, _} ->
350		    {ok, Port}
351	    end
352    catch
353	error:Reason ->
354	    {error, Reason}
355    end.
356
357call_port(Port, Command) when is_port(Port), is_binary(Command) ->
358    try erlang:port_command(Port, Command) of
359	true ->
360	    get_response(Port)
361    catch
362	error:badarg ->
363	    {error, einval}; % Since Command is valid, Port must be dead
364	error:Reason ->
365	    {error, Reason}
366    end;
367call_port(Port, Command) ->
368    try erlang:iolist_to_binary(Command) of
369	Bin ->
370	    call_port(Port, Bin)
371    catch
372	error:Reason ->
373	    {error, Reason}
374    end.
375
376get_response(Port) ->
377    receive
378	{Port, {data, [Response|Rest]}} ->
379	    translate_response(Response, Rest);
380	{'EXIT', Port, _Reason} ->
381	    {error, port_died}
382    end.
383
384ll_close(Port) ->
385    try erlang:port_close(Port) catch error:_ -> ok end,
386    receive %% In case the caller is the owner and traps exits
387	{'EXIT', Port, _} ->
388	    ok
389    after 0 ->
390	    ok
391    end.
392
393%%%-----------------------------------------------------------------
394%%% Utility functions.
395
396%% Converts a list of mode atoms into an mode word for the driver.
397%% Returns {Mode, Opts} wher Opts is a list of options for
398%% erlang:open_port/2, or {error, einval} upon failure.
399
400open_mode(List) when is_list(List) ->
401    case open_mode(List, {0, []}) of
402	{Mode, Opts} when Mode band
403			  (?RAM_FILE_MODE_READ bor ?RAM_FILE_MODE_WRITE)
404			  =:= 0 ->
405	    {Mode bor ?RAM_FILE_MODE_READ, Opts};
406	Other ->
407	    Other
408    end.
409
410open_mode([ram|Rest], {Mode, Opts}) ->
411    open_mode(Rest, {Mode, Opts});
412open_mode([read|Rest], {Mode, Opts}) ->
413    open_mode(Rest, {Mode bor ?RAM_FILE_MODE_READ, Opts});
414open_mode([write|Rest], {Mode, Opts}) ->
415    open_mode(Rest, {Mode bor ?RAM_FILE_MODE_WRITE, Opts});
416open_mode([binary|Rest], {Mode, Opts}) ->
417    open_mode(Rest, {Mode, [binary | Opts]});
418open_mode([], {Mode, Opts}) ->
419    {Mode, Opts};
420open_mode(_, _) ->
421    {error, badarg}.
422
423
424
425%% Converts a position tuple {bof, X} | {cur, X} | {eof, X} into
426%% {ok, Offset, OriginCode} for the driver.
427%% Returns {error, einval} upon failure.
428
429lseek_position(Pos) when is_integer(Pos) ->
430    lseek_position({bof, Pos});
431lseek_position(bof) ->
432    lseek_position({bof, 0});
433lseek_position(cur) ->
434    lseek_position({cur, 0});
435lseek_position(eof) ->
436    lseek_position({eof, 0});
437lseek_position({bof, Offset}) when is_integer(Offset) ->
438    {ok, Offset, ?RAM_FILE_SEEK_SET};
439lseek_position({cur, Offset}) when is_integer(Offset) ->
440    {ok, Offset, ?RAM_FILE_SEEK_CUR};
441lseek_position({eof, Offset}) when is_integer(Offset) ->
442    {ok, Offset, ?RAM_FILE_SEEK_END};
443lseek_position(_) ->
444    {error, badarg}.
445
446
447
448translate_response(?RAM_FILE_RESP_OK, []) ->
449    ok;
450translate_response(?RAM_FILE_RESP_OK, Data) ->
451    {ok, Data};
452translate_response(?RAM_FILE_RESP_ERROR, List) when is_list(List) ->
453    {error, list_to_atom(List)};
454translate_response(?RAM_FILE_RESP_NUMBER, [X1, X2, X3, X4]) ->
455    {ok, i32(X1, X2, X3, X4)};
456translate_response(?RAM_FILE_RESP_DATA, [X1, X2, X3, X4|Data]) ->
457    {ok, {i32(X1, X2, X3, X4), Data}};
458translate_response(X, Data) ->
459    {error, {bad_response_from_port, X, Data}}.
460
461i32(X1,X2,X3,X4) ->
462    (X1 bsl 24) bor (X2 bsl 16) bor (X3 bsl 8) bor X4.
463