1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 1996-2020. 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
21%% This is a development feature when developing a new file module,
22%% ugly but practical.
23-ifndef(FILE_MODULE).
24-define(FILE_MODULE, file).
25-endif.
26-ifndef(FILE_SUITE).
27-define(FILE_SUITE, file_SUITE).
28-endif.
29-ifndef(FILE_INIT).
30-define(FILE_INIT(Config), Config).
31-endif.
32-ifndef(FILE_FINI).
33-define(FILE_FINI(Config), Config).
34-endif.
35-ifndef(FILE_INIT_PER_TESTCASE).
36-define(FILE_INIT_PER_TESTCASE(Config), Config).
37-endif.
38-ifndef(FILE_FIN_PER_TESTCASE).
39-define(FILE_FIN_PER_TESTCASE(Config), Config).
40-endif.
41
42-define(PRIM_FILE, prim_file).
43
44-module(?FILE_SUITE).
45
46-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
47	 init_per_group/2,end_per_group/2,
48	 init_per_testcase/2, end_per_testcase/2,
49	 read_write_file/1, names/1]).
50-export([cur_dir_0/1, cur_dir_1/1, make_del_dir/1,
51	 list_dir/1,list_dir_error/1,
52	 untranslatable_names/1, untranslatable_names_error/1,
53	 pos1/1, pos2/1, pos3/1]).
54-export([close/1, consult1/1, path_consult/1, delete/1]).
55-export([ eval1/1, path_eval/1, script1/1, path_script/1,
56	  open1/1,
57	  old_modes/1, new_modes/1, path_open/1, open_errors/1]).
58-export([ file_info_basic_file/1, file_info_basic_directory/1,
59	  file_info_bad/1, file_info_times/1, file_write_file_info/1,
60          file_wfi_helpers/1]).
61-export([rename/1, access/1, truncate/1, datasync/1, sync/1,
62	 read_write/1, pread_write/1, append/1, exclusive/1]).
63-export([ e_delete/1, e_rename/1, e_make_dir/1, e_del_dir/1]).
64-export([otp_5814/1, otp_10852/1]).
65
66-export([ read_not_really_compressed/1,
67	  read_compressed_cooked/1, read_compressed_cooked_binary/1,
68	  read_cooked_tar_problem/1,
69	  write_compressed/1, compress_errors/1, catenated_gzips/1,
70	  compress_async_crash/1]).
71
72-export([ make_link/1, read_link_info_for_non_link/1, symlinks/1]).
73
74-export([copy/1]).
75
76-export([new_slave/2, old_slave/2, run_test/2]).
77
78-export([delayed_write/1, read_ahead/1, segment_read/1, segment_write/1]).
79
80-export([ipread/1]).
81
82-export([pid2name/1]).
83
84-export([interleaved_read_write/1]).
85
86-export([unicode/1]).
87-export([altname/1]).
88
89-export([large_file/0, large_file/1, large_write/0, large_write/1]).
90
91-export([read_line_1/1, read_line_2/1, read_line_3/1,read_line_4/1]).
92
93-export([advise/1]).
94
95-export([allocate/1]).
96
97-export([allocate_file_size/1]).
98
99-export([standard_io/1,mini_server/1]).
100
101-export([old_io_protocol/1]).
102
103-export([unicode_mode/1]).
104
105-export([volume_relative_paths/1,unc_paths/1]).
106
107-export([tiny_writes/1, tiny_writes_delayed/1,
108         large_writes/1, large_writes_delayed/1,
109         tiny_reads/1, tiny_reads_ahead/1]).
110
111%% Debug exports
112-export([create_file_slow/2, create_file/2, create_bin/2]).
113-export([verify_file/2, verify_bin/3]).
114-export([bytes/2, iterate/3]).
115
116
117%% System probe functions that might be handy to check from the shell
118-export([disc_free/1, memsize/0]).
119
120-include_lib("common_test/include/ct.hrl").
121-include_lib("common_test/include/ct_event.hrl").
122
123-include_lib("kernel/include/file.hrl").
124
125-define(THROW_ERROR(RES), throw({fail, ?LINE, RES})).
126
127
128suite() ->
129    [{ct_hooks,[ts_install_cth]},
130     {timetrap,{minutes,1}}].
131
132all() ->
133    [unicode, altname, read_write_file, {group, dirs},
134     {group, files}, delete, rename, names, volume_relative_paths, unc_paths,
135     {group, errors}, {group, compression}, {group, links}, copy,
136     delayed_write, read_ahead, segment_read, segment_write,
137     ipread, pid2name, interleaved_read_write, otp_5814, otp_10852,
138     large_file, large_write, read_line_1, read_line_2, read_line_3,
139     read_line_4, standard_io, old_io_protocol,
140     unicode_mode, {group, bench}
141    ].
142
143groups() ->
144    [{dirs, [], [make_del_dir, cur_dir_0, cur_dir_1,
145		 list_dir, list_dir_error, untranslatable_names,
146		 untranslatable_names_error]},
147     {files, [],
148      [{group, open}, {group, pos}, {group, file_info},
149       {group, consult}, {group, eval}, {group, script},
150       truncate, sync, datasync, advise, allocate, allocate_file_size]},
151     {open, [],
152      [open1, old_modes, new_modes, path_open, close, access,
153       read_write, pread_write, append, open_errors,
154       exclusive]},
155     {pos, [], [pos1, pos2, pos3]},
156     {file_info, [],
157      [file_info_basic_file, file_info_basic_directory,
158       file_info_bad, file_info_times, file_write_file_info,
159       file_wfi_helpers]},
160     {consult, [], [consult1, path_consult]},
161     {eval, [], [eval1, path_eval]},
162     {script, [], [script1, path_script]},
163     {errors, [],
164      [e_delete, e_rename, e_make_dir, e_del_dir]},
165     {compression, [],
166      [read_compressed_cooked, read_compressed_cooked_binary,
167       read_cooked_tar_problem, read_not_really_compressed,
168       write_compressed, compress_errors, catenated_gzips,
169       compress_async_crash]},
170     {links, [],
171      [make_link, read_link_info_for_non_link, symlinks]},
172     {bench, [],
173      [tiny_writes, tiny_writes_delayed,
174       large_writes, large_writes_delayed,
175       tiny_reads, tiny_reads_ahead]}].
176
177init_per_group(_GroupName, Config) ->
178    Config.
179
180end_per_group(bench, Config) ->
181    ScratchDir = proplists:get_value(priv_dir, Config),
182    file:delete(filename:join(ScratchDir, "benchmark_scratch_file")),
183    Config;
184end_per_group(_GroupName, Config) ->
185    Config.
186
187
188init_per_suite(Config) when is_list(Config) ->
189    SaslConfig = case application:start(sasl) of
190		     {error, {already_started, sasl}} ->
191			 [];
192		     ok ->
193			 [{sasl,started}]
194		 end,
195    application:start(os_mon),
196
197    case os:type() of
198	{win32, _} ->
199	    Priv = proplists:get_value(priv_dir, Config),
200	    HasAccessTime =
201		case ?FILE_MODULE:read_file_info(Priv) of
202		    {ok, #file_info{atime={_, {0, 0, 0}}}} ->
203			%% This is a unfortunately a FAT file system.
204			[no_access_time];
205		    {ok, _} ->
206			[]
207		end,
208	    ?FILE_INIT(HasAccessTime++Config++SaslConfig);
209	_ ->
210	    ?FILE_INIT(Config++SaslConfig)
211    end.
212
213end_per_suite(Config) when is_list(Config) ->
214    case os:type() of
215	{win32, _} ->
216	    os:cmd("subst z: /d");
217	_ ->
218	    ok
219    end,
220
221    application:stop(os_mon),
222    case proplists:get_value(sasl, Config) of
223	started ->
224	    application:stop(sasl);
225	_Else ->
226	    ok
227    end,
228    ?FILE_FINI(Config).
229
230init_per_testcase(_Func, Config) ->
231    %%error_logger:info_msg("~p:~p *****~n", [?MODULE, _Func]),
232    ?FILE_INIT_PER_TESTCASE(Config).
233
234end_per_testcase(_Func, Config) ->
235    %% error_logger:info_msg("~p:~p END *****~n", [?MODULE, _Func]),
236    ?FILE_FIN_PER_TESTCASE(Config).
237
238%% Matches a term (the last) against alternatives
239expect(X, _, X) ->
240    X;
241expect(_, X, X) ->
242    X.
243
244expect(X, _, _, X) ->
245    X;
246expect(_, X, _, X) ->
247    X;
248expect(_, _, X, X) ->
249    X.
250
251expect(X, _, _, _, X) ->
252    X;
253expect(_, X, _, _, X) ->
254    X;
255expect(_, _, X, _, X) ->
256    X;
257expect(_, _, _, X, X) ->
258    X.
259
260%% Calculate the time difference
261time_dist({YY, MM, DD, H, M, S}, DT) ->
262    time_dist({{YY, MM, DD}, {H, M, S}}, DT);
263time_dist(DT, {YY, MM, DD, H, M, S}) ->
264    time_dist(DT, {{YY, MM, DD}, {H, M, S}});
265time_dist({_D1, _T1} = DT1, {_D2, _T2} = DT2) ->
266    calendar:datetime_to_gregorian_seconds(DT2)
267	- calendar:datetime_to_gregorian_seconds(DT1).
268
269%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
270mini_server(Parent) ->
271    receive
272	die ->
273	    ok;
274	{io_request,From,To,{put_chars,_Encoding,Data}} ->
275	    Parent ! {io_request,From,To,{put_chars,Data}},
276	    From ! {io_reply, To, ok},
277	    mini_server(Parent);
278	{io_request,From,To,{get_chars,'',N}} ->
279	    Parent ! {io_request,From,To,{get_chars,'',N}},
280	    From ! {io_reply, To, {ok, lists:duplicate(N,$a)}},
281	    mini_server(Parent);
282	{io_request,From,To,{get_line,''}} ->
283	    Parent ! {io_request,From,To,{get_line,''}},
284	    From ! {io_reply, To, {ok, "hej\n"}},
285	    mini_server(Parent)
286    end.
287
288%% Test that standard i/o-servers work with file module.
289standard_io(Config) when is_list(Config) ->
290    %% Really just a smoke test
291    Pid = spawn(?MODULE,mini_server,[self()]),
292    register(mini_server,Pid),
293    ok = file:write(mini_server,<<"hej\n">>),
294    receive
295	{io_request,_,_,{put_chars,<<"hej\n">>}} ->
296	    ok
297    after 1000 ->
298	    exit(noreply)
299    end,
300    {ok,"aaaaa"} = file:read(mini_server,5),
301    receive
302	{io_request,_,_,{get_chars,'',5}} ->
303	    ok
304    after 1000 ->
305	    exit(noreply)
306    end,
307    {ok,"hej\n"} = file:read_line(mini_server),
308    receive
309	{io_request,_,_,{get_line,''}} ->
310	    ok
311    after 1000 ->
312	    exit(noreply)
313    end,
314    OldGL = group_leader(),
315    group_leader(Pid,self()),
316    ok = file:write(standard_io,<<"hej\n">>),
317    group_leader(OldGL,self()),
318    receive
319	{io_request,_,_,{put_chars,<<"hej\n">>}} ->
320	    ok
321    after 1000 ->
322	    exit(noreply)
323    end,
324    group_leader(Pid,self()),
325    {ok,"aaaaa"} = file:read(standard_io,5),
326    group_leader(OldGL,self()),
327    receive
328	{io_request,_,_,{get_chars,'',5}} ->
329	    ok
330    after 1000 ->
331	    exit(noreply)
332    end,
333    group_leader(Pid,self()),
334    {ok,"hej\n"} = file:read_line(standard_io),
335    group_leader(OldGL,self()),
336    receive
337	{io_request,_,_,{get_line,''}} ->
338	    ok
339    after 1000 ->
340	    exit(noreply)
341    end,
342    Pid ! die,
343    receive after 1000 -> ok end.
344
345%% Test that the old file IO protocol =< R16B still works.
346old_io_protocol(Config) when is_list(Config) ->
347    RootDir = proplists:get_value(priv_dir,Config),
348    Name = filename:join(RootDir,
349			 atom_to_list(?MODULE)
350			 ++"old_io_protocol.fil"),
351    MyData = "0123456789abcdefghijklmnopqrstuvxyz",
352    ok = ?FILE_MODULE:write_file(Name, MyData),
353    {ok, Fd} = ?FILE_MODULE:open(Name, write),
354    Fd ! {file_request,self(),Fd,truncate},
355    receive
356	{file_reply,Fd,ok} -> ok
357    end,
358    ok = ?FILE_MODULE:close(Fd),
359    {ok, <<>>} = ?FILE_MODULE:read_file(Name),
360    [] = flush(),
361    ok.
362
363unicode_mode(Config) ->
364    Dir = {dir, proplists:get_value(priv_dir,Config)},
365    OptVariants = [[Dir],
366		   [Dir, {encoding, utf8}],
367		   [Dir, binary],
368		   [Dir, binary, {encoding, utf8}]
369		  ],
370    ReadVariants = [{read, fun(Fd) -> um_read(Fd, fun(Fd1) -> file:read(Fd1, 1024) end) end},
371		    {read_line, fun(Fd) -> um_read(Fd, fun(Fd1) -> file:read_line(Fd1) end) end}
372		    %%{pread, fun(Fd) -> file:pread(Fd, 0, 1024) end},
373		    %%{preadl, fun(Fd) -> file:pread(Fd, [{0, 1024}]) end},
374		   ],
375
376    _ = [read_write_0("ASCII: list:  Hello World", Read, Opt) ||
377	    Opt <- OptVariants, Read <- ReadVariants],
378    _ = [read_write_0("LATIN1: list: åäöÅÄÖ", Read, Opt) ||
379	    Opt <- OptVariants, Read <- ReadVariants],
380    _ = [read_write_0(<<"ASCII: bin: Hello World">>, Read, Opt) ||
381	    Opt <- OptVariants, Read <- ReadVariants],
382    _ = [read_write_0(<<"LATIN1: bin: åäöÅÄÖ">>, Read, Opt) ||
383	    Opt <- OptVariants, Read <- ReadVariants],
384    %% These will be double encoded if option is encoding utf-8
385    _ = [read_write_0(<<"UTF8: bin: Ωß"/utf8>>, Read, Opt) ||
386	    Opt <- OptVariants, Read <- ReadVariants],
387    %% These should not work (with encoding set to utf-8)
388    %%   according to file's documentation
389    _ = [read_write_0("UTF8: list: Ωß", Read, Opt) ||
390	    Opt <- OptVariants, Read <- ReadVariants],
391    ok.
392
393read_write_0(Str, {Func, ReadFun}, Options) ->
394    try
395	Res = read_write_1(Str, ReadFun, Options),
396	io:format("~p: ~ts ~p '~p'~n", [Func, Str, tl(Options), Res]),
397	ok
398    catch {fail, Line, ReadBytes = [_|_]} ->
399	    io:format("~p:~p: ~p ERROR: ~w vs~n             ~w~n  - ~p~n",
400		      [?MODULE, Line, Func, Str, ReadBytes, Options]),
401	    exit({error, ?LINE});
402	  {fail, Line, ReadBytes} ->
403	    io:format("~p:~p: ~p ERROR: ~ts vs~n             ~w~n  - ~p~n",
404		      [?MODULE, Line, Func, Str, ReadBytes, Options]),
405	    exit({error, ?LINE});
406	  error:What:Stacktrace ->
407	    io:format("~p:??: ~p ERROR: ~p from~n  ~w~n  ~p~n",
408		      [?MODULE, Func, What, Str, Options]),
409
410	    io:format("\t~p~n", [Stacktrace]),
411	    exit({error, ?LINE})
412    end.
413
414read_write_1(Str0, ReadFun, [{dir,Dir}|Options]) ->
415    File = um_filename(Str0, Dir, Options),
416    Pre = "line 1\n", Post = "\nlast line\n",
417    Str = case is_list(Str0) andalso lists:max(Str0) > 255 of
418	      false ->  %% Normal case Use options
419		  {ok, FdW} = file:open(File, [write|Options]),
420		  IO = [Pre, Str0, Post],
421		  ok = file:write(FdW, IO),
422		  case is_binary(Str0) of
423		      true -> iolist_to_binary(IO);
424		      false -> lists:append(IO)
425		  end;
426	      true -> %% Test unicode lists
427		  {ok, FdW} = file:open(File, [write]),
428		  Utf8 = unicode:characters_to_binary([Pre, Str0, Post]),
429		  file:write(FdW, Utf8),
430		  {unicode, Utf8}
431	  end,
432    file:close(FdW),
433    {ok, FdR} = file:open(File, [read|Options]),
434    ReadRes = ReadFun(FdR),
435    file:close(FdR),
436    Res = um_check(Str, ReadRes, Options),
437    file:delete(File),
438    Res.
439
440
441um_read(Fd, Fun) ->
442    um_read(Fd, Fun, []).
443
444um_read(Fd, Fun, Acc) ->
445    case Fun(Fd) of
446	eof ->
447	    case is_binary(hd(Acc)) of
448		true  -> {ok, iolist_to_binary(lists:reverse(Acc))};
449		false -> {ok, lists:append(lists:reverse(Acc))}
450	    end;
451	{ok, Data} ->
452	    um_read(Fd, Fun, [Data|Acc]);
453	Error ->
454	    Error
455    end.
456
457
458um_check(Str, {ok, Str}, _) -> ok;
459um_check(Bin, {ok, Res}, _Options) when is_binary(Bin), is_list(Res) ->
460    case list_to_binary(Res) of
461	Bin -> ok;
462	_ -> ?THROW_ERROR(Res)
463    end;
464um_check(Str, {ok, Res}, _Options) when is_list(Str), is_binary(Res) ->
465    case iolist_to_binary(Str) of
466	Res -> ok;
467	_ -> ?THROW_ERROR(Res)
468    end;
469um_check({unicode, Utf8Bin}, Res, Options) ->
470    um_check_unicode(Utf8Bin, Res,
471		     proplists:get_value(binary, Options, false),
472		     proplists:get_value(encoding, Options, none));
473um_check(_Str, Res, _Options) ->
474    ?THROW_ERROR(Res).
475
476um_check_unicode(Utf8Bin, {ok, Utf8Bin}, true, none) ->
477    ok;
478um_check_unicode(Utf8Bin, {ok, List = [_|_]}, false, none) ->
479    case binary_to_list(Utf8Bin) == List of
480	true -> ok;
481	false -> ?THROW_ERROR(List)
482    end;
483um_check_unicode(_Utf8Bin, {error, {no_translation, unicode, latin1}}, _, _) ->
484    no_translation;
485um_check_unicode(_Utf8Bin, Error = {error, _}, _, _Unicode) ->
486    ?THROW_ERROR(Error);
487um_check_unicode(_Utf8Bin, {ok, _ListOrBin}, _, _UTF8_) ->
488    %% List = if is_binary(ListOrBin) -> unicode:characters_to_list(ListOrBin);
489    %% 	      true -> ListOrBin
490    %% 	   end,
491    %% io:format("In: ~w~n", [binary_to_list(Utf8Bin)]),
492    %% io:format("Ut: ~w~n", [List]),
493    ?THROW_ERROR({shoud_be, no_translation}).
494
495um_filename(Bin, Dir, Options) when is_binary(Bin) ->
496    um_filename(binary_to_list(Bin), Dir, Options);
497um_filename(Str = [_|_], Dir, Options) ->
498    Name = hd(string:lexemes(Str, ":")),
499    Enc = atom_to_list(proplists:get_value(encoding, Options, latin1)),
500    File = case lists:member(binary, Options) of
501	       true ->
502		   "test_" ++ Name ++ "_bin_enc_" ++ Enc;
503	       false ->
504		   "test_" ++ Name ++ "_list_enc_" ++ Enc
505	   end,
506    filename:join(Dir, File).
507
508%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
509
510read_write_file(Config) when is_list(Config) ->
511    RootDir = proplists:get_value(priv_dir,Config),
512    Name = filename:join(RootDir,
513			 atom_to_list(?MODULE)
514			 ++"_read_write_file"),
515
516    %% Try writing and reading back some term
517    SomeTerm = {"This term",{will,be},[written,$t,$o],1,file,[]},
518    Bin1 = term_to_binary(SomeTerm),
519    ok = do_read_write_file(Name, Bin1),
520
521    %% Try a "null" term
522    NullTerm = [],
523    Bin2 = term_to_binary(NullTerm),
524    ok = do_read_write_file(Name, Bin2),
525
526    %% Try reading a nonexistent file
527    Name2 = filename:join(RootDir,
528			  atom_to_list(?MODULE)
529			  ++"_nonexistent_file"),
530    {error, enoent} = ?FILE_MODULE:read_file(Name2),
531    {error, enoent} = ?FILE_MODULE:read_file(""),
532    {error, enoent} = ?FILE_MODULE:read_file(''),
533
534    %% Try writing to a bad filename
535    {error, enoent} = do_read_write_file("", Bin2),
536
537    %% Try writing something else than a binary
538    {error, badarg} = do_read_write_file(Name, {1,2,3}),
539    {error, badarg} = do_read_write_file(Name, self()),
540
541    %% Some non-term binaries
542    ok = do_read_write_file(Name, []),
543
544    %% Write some iolists
545    ok = do_read_write_file(Name, [Bin1,[],[[Bin2]]]),
546    ok = do_read_write_file(Name, ["string",<<"binary">>]),
547    ok = do_read_write_file(Name, "pure string"),
548
549    [] = flush(),
550    ok.
551
552do_read_write_file(Name, Data) ->
553    case ?FILE_MODULE:write_file(Name, Data) of
554	ok ->
555	    BinData = iolist_to_binary(Data),
556	    {ok,BinData} = ?FILE_MODULE:read_file(Name),
557
558	    ok = ?FILE_MODULE:write_file(Name, Data, []),
559	    {ok,BinData} = ?FILE_MODULE:read_file(Name),
560
561	    ok = ?FILE_MODULE:write_file(Name, Data, [raw]),
562	    {ok,BinData} = ?FILE_MODULE:read_file(Name),
563
564	    ok;
565	{error,_}=Res ->
566	    Res = ?FILE_MODULE:write_file(Name, Data, []),
567	    Res = ?FILE_MODULE:write_file(Name, Data, [raw]),
568	    Res
569    end.
570
571%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
572
573
574make_del_dir(Config) when is_list(Config) ->
575    RootDir = proplists:get_value(priv_dir,Config),
576    NewDir = filename:join(RootDir,
577			   atom_to_list(?MODULE)
578			   ++"_mk-dir"),
579    ok = ?FILE_MODULE:make_dir(NewDir),
580    {error, eexist} = ?FILE_MODULE:make_dir(NewDir),
581    ok = ?FILE_MODULE:del_dir(NewDir),
582    {error, enoent} = ?FILE_MODULE:del_dir(NewDir),
583    %% Make sure we are not in a directory directly under test_server
584    %% as that would result in eacces errors when trying to delete '..',
585    %% because there are processes having that directory as current.
586    ok = ?FILE_MODULE:make_dir(NewDir),
587    {ok,CurrentDir} = file:get_cwd(),
588    case {os:type(), length(NewDir) >= 260 } of
589	{{win32,_}, true} ->
590	    io:format("Skip set_cwd for windows path longer than 260 (MAX_PATH)\n", []),
591	    io:format("\nNewDir = ~p\n", [NewDir]);
592    	_ ->
593	    ok = ?FILE_MODULE:set_cwd(NewDir)
594    end,
595    try
596	%% Check that we get an error when trying to create...
597	%% a deep directory
598	NewDir2 = filename:join(RootDir,
599				atom_to_list(?MODULE)
600				++"_mk-dir-noexist/foo"),
601	{error, enoent} = ?FILE_MODULE:make_dir(NewDir2),
602	%% a nameless directory
603	{error, enoent} = ?FILE_MODULE:make_dir(""),
604	%% a directory with illegal name
605	{error, badarg} = ?FILE_MODULE:make_dir({1,2,3}),
606
607	%% a directory with illegal name, even if it's a (bad) list
608	{error, badarg} = ?FILE_MODULE:make_dir([1,2,3,{}]),
609
610	%% Maybe this isn't an error, exactly, but worth mentioning anyway:
611	%% ok = ?FILE_MODULE:make_dir([$f,$o,$o,0,$b,$a,$r])),
612	%% The above line works, and created a directory "./foo"
613	%% More elegant would maybe have been to fail, or to really create
614	%% a directory, but with a name that incorporates the "bar" part of
615	%% the list, so that [$f,$o,$o,0,$f,$o,$o] wouldn't refer to the same
616	%% dir. But this would slow it down.
617
618	%% Try deleting some bad directories
619	%% Deleting the parent directory to the current, sounds dangerous, huh?
620	%% Don't worry ;-) the parent directory should never be empty, right?
621	case ?FILE_MODULE:del_dir('..') of
622	    {error, eexist} -> ok;
623	    {error, eacces} -> ok;		%OpenBSD
624	    {error, einval} -> ok			%FreeBSD
625	end,
626	{error, enoent} = ?FILE_MODULE:del_dir(""),
627	{error, badarg} = ?FILE_MODULE:del_dir([3,2,1,{}]),
628
629	[] = flush()
630    after
631	?FILE_MODULE:set_cwd(CurrentDir)
632    end,
633    ok.
634
635cur_dir_0(Config) when is_list(Config) ->
636    %% Find out the current dir, and cd to it ;-)
637    {ok,BaseDir} = ?FILE_MODULE:get_cwd(),
638    Dir1 = BaseDir ++ "", %% Check that it's a string
639    ok = ?FILE_MODULE:set_cwd(Dir1),
640
641    %% Make a new dir, and cd to that
642    RootDir = proplists:get_value(priv_dir,Config),
643    NewDir = filename:join(RootDir,
644			   atom_to_list(?MODULE)
645			   ++"_curdir"),
646    ok = ?FILE_MODULE:make_dir(NewDir),
647    case {os:type(), length(NewDir) >= 260} of
648	{{win32,_}, true} ->
649	    io:format("Skip set_cwd for windows path longer than 260 (MAX_PATH):\n"),
650	    io:format("\nNewDir = ~p\n", [NewDir]);
651	_ ->
652	    io:format("cd to ~s",[NewDir]),
653    	    ok = ?FILE_MODULE:set_cwd(NewDir),
654
655	    %% Create a file in the new current directory, and check that it
656	    %% really is created there
657	    UncommonName = "uncommon.fil",
658	    {ok,Fd} = ?FILE_MODULE:open(UncommonName,read_write),
659	    ok = ?FILE_MODULE:close(Fd),
660	    {ok,NewDirFiles} = ?FILE_MODULE:list_dir("."),
661	    true = lists:member(UncommonName,NewDirFiles),
662
663	    %% Ensure that we get the same result with a trailing slash; the
664	    %% APIs used on Windows will choke on them if passed directly.
665	    {ok,NewDirFiles} = ?FILE_MODULE:list_dir("./"),
666
667	    %% Delete the directory and return to the old current directory
668	    %% and check that the created file isn't there (too!)
669	    expect({error, einval}, {error, eacces},
670	    	   ?FILE_MODULE:del_dir(NewDir)),
671	    ?FILE_MODULE:delete(UncommonName),
672	    {ok,[]} = ?FILE_MODULE:list_dir("."),
673	    ok = ?FILE_MODULE:set_cwd(Dir1),
674	    io:format("cd back to ~s",[Dir1]),
675
676	    ok = ?FILE_MODULE:del_dir(NewDir),
677	    {error, enoent} = ?FILE_MODULE:set_cwd(NewDir),
678	    ok = ?FILE_MODULE:set_cwd(Dir1),
679	    io:format("cd back to ~s",[Dir1]),
680	    {ok,OldDirFiles} = ?FILE_MODULE:list_dir("."),
681	    false = lists:member(UncommonName,OldDirFiles)
682    end,
683
684    %% Try doing some bad things
685    {error, badarg} = ?FILE_MODULE:set_cwd({foo,bar}),
686    {error, enoent} = ?FILE_MODULE:set_cwd(""),
687    {error, enoent} = ?FILE_MODULE:set_cwd(".......a......"),
688    {ok,BaseDir} = ?FILE_MODULE:get_cwd(), %% Still there?
689
690    %% On Windows, there should only be slashes, no backslashes,
691    %% in the return value of get_cwd().
692    %% (The test is harmless on Unix, because filenames usually
693    %% don't contain backslashes.)
694
695    {ok, BaseDir} = ?FILE_MODULE:get_cwd(),
696    false = lists:member($\\, BaseDir),
697
698    [] = flush(),
699    ok.
700
701%% Tests ?FILE_MODULE:get_cwd/1.
702
703cur_dir_1(Config) when is_list(Config) ->
704    case os:type() of
705	{win32, _} ->
706	    win_cur_dir_1(Config);
707	_ ->
708	    {error, enotsup} = ?FILE_MODULE:get_cwd("d:")
709    end,
710    [] = flush(),
711    ok.
712
713win_cur_dir_1(_Config) ->
714    {ok,BaseDir} = ?FILE_MODULE:get_cwd(),
715
716    %% Get the drive letter from the current directory,
717    %% and try to get current directory for that drive.
718
719    [CurDrive,$:|_] = BaseDir,
720    {ok,BaseDir} = ?FILE_MODULE:get_cwd([CurDrive,$:]),
721    io:format("BaseDir = ~s\n", [BaseDir]),
722
723    %% We should error out on non-existent drives. Any reasonable system will
724    %% have at least one.
725    CurDirs = [?FILE_MODULE:get_cwd([Drive,$:]) || Drive <- lists:seq($A, $Z)],
726    lists:member({error,eaccess}, CurDirs),
727
728    %% Unfortunately, there is no way to move away from the
729    %% current drive as we can't use the "subst" command from
730    %% a SSH connection. We can't test any more.
731
732    ok.
733
734
735%%%
736%%% Test list_dir() on a non-existing pathname.
737%%%
738
739list_dir_error(Config) ->
740    Priv = proplists:get_value(priv_dir, Config),
741    NonExisting = filename:join(Priv, "non-existing-dir"),
742    {error,enoent} = ?FILE_MODULE:list_dir(NonExisting),
743    ok.
744
745%%%
746%%% Test list_dir() and list_dir_all().
747%%%
748
749list_dir(Config) ->
750    RootDir = proplists:get_value(priv_dir, Config),
751    TestDir = filename:join(RootDir, ?MODULE_STRING++"_list_dir"),
752    ?FILE_MODULE:make_dir(TestDir),
753    list_dir_1(TestDir, 42, []).
754
755list_dir_1(TestDir, 0, Sorted) ->
756    [ok = ?FILE_MODULE:delete(filename:join(TestDir, F)) ||
757	F <- Sorted],
758    ok = ?FILE_MODULE:del_dir(TestDir);
759list_dir_1(TestDir, Cnt, Sorted0) ->
760    Base = "file" ++ integer_to_list(Cnt),
761    Name = filename:join(TestDir, Base),
762    ok = ?FILE_MODULE:write_file(Name, Base),
763    Sorted = lists:merge([Base], Sorted0),
764    {ok,DirList0} = ?FILE_MODULE:list_dir(TestDir),
765    {ok,DirList1} = ?FILE_MODULE:list_dir_all(TestDir),
766    Sorted = lists:sort(DirList0),
767    Sorted = lists:sort(DirList1),
768    list_dir_1(TestDir, Cnt-1, Sorted).
769
770untranslatable_names(Config) ->
771    case no_untranslatable_names() of
772	true ->
773	    {skip,"Not a problem on this OS"};
774	false ->
775	    untranslatable_names_1(Config)
776    end.
777
778untranslatable_names_1(Config) ->
779    {ok,OldCwd} = file:get_cwd(),
780    PrivDir = proplists:get_value(priv_dir, Config),
781    Dir = filename:join(PrivDir, "untranslatable_names"),
782    ok = file:make_dir(Dir),
783    Node = start_node(untranslatable_names, "+fnu"),
784    try
785	ok = file:set_cwd(Dir),
786	[ok = file:write_file(F, F) || {_,F} <- untranslatable_names()],
787
788	ExpectedListDir0 = [unicode:characters_to_list(N, utf8) ||
789			       {utf8,N} <- untranslatable_names()],
790	ExpectedListDir = lists:sort(ExpectedListDir0),
791	io:format("ExpectedListDir: ~p\n", [ExpectedListDir]),
792	ExpectedListDir = call_and_sort(Node, file, list_dir, [Dir]),
793
794	ExpectedListDirAll0 = [case Enc of
795				   utf8 ->
796				       unicode:characters_to_list(N, utf8);
797				   latin1 ->
798				       N
799			       end || {Enc,N} <- untranslatable_names()],
800	ExpectedListDirAll = lists:sort(ExpectedListDirAll0),
801	io:format("ExpectedListDirAll: ~p\n", [ExpectedListDirAll]),
802	ExpectedListDirAll = call_and_sort(Node, file, list_dir_all, [Dir])
803    after
804	catch test_server:stop_node(Node),
805	file:set_cwd(OldCwd),
806	[file:delete(F) || {_,F} <- untranslatable_names()],
807	file:del_dir(Dir)
808    end,
809    ok.
810
811untranslatable_names_error(Config) ->
812    case no_untranslatable_names() of
813	true ->
814	    {skip,"Not a problem on this OS"};
815	false ->
816	    untranslatable_names_error_1(Config)
817    end.
818
819untranslatable_names_error_1(Config) ->
820    {ok,OldCwd} = file:get_cwd(),
821    PrivDir = proplists:get_value(priv_dir, Config),
822    Dir = filename:join(PrivDir, "untranslatable_names_error"),
823    ok = file:make_dir(Dir),
824    Node = start_node(untranslatable_names, "+fnue"),
825    try
826	ok = file:set_cwd(Dir),
827	[ok = file:write_file(F, F) || {_,F} <- untranslatable_names()],
828
829	ExpectedListDir0 = [unicode:characters_to_list(N, utf8) ||
830			       {utf8,N} <- untranslatable_names()],
831	ExpectedListDir = lists:sort(ExpectedListDir0),
832	io:format("ExpectedListDir: ~p\n", [ExpectedListDir]),
833	{error,{no_translation,BadFile}} =
834	    rpc:call(Node, file, list_dir, [Dir]),
835	true = lists:keymember(BadFile, 2, untranslatable_names())
836
837    after
838	catch test_server:stop_node(Node),
839	file:set_cwd(OldCwd),
840	[file:delete(F) || {_,F} <- untranslatable_names()],
841	file:del_dir(Dir)
842    end,
843    ok.
844
845untranslatable_names() ->
846    [{utf8,<<"abc">>},
847     {utf8,<<"def">>},
848     {utf8,<<"Lagerl",195,182,"f">>},
849     {utf8,<<195,150,"stra Emterwik">>},
850     {latin1,<<"M",229,"rbacka">>},
851     {latin1,<<"V",228,"rmland">>}].
852
853call_and_sort(Node, M, F, A) ->
854    {ok,Res} = rpc:call(Node, M, F, A),
855    lists:sort(Res).
856
857no_untranslatable_names() ->
858    case os:type() of
859	{unix,darwin} -> true;
860	{win32,_} -> true;
861	_ -> false
862    end.
863
864start_node(Name, Args) ->
865    [_,Host] = string:lexemes(atom_to_list(node()), "@"),
866    ct:log("Trying to start ~w@~s~n", [Name,Host]),
867    case test_server:start_node(Name, peer, [{args,Args}]) of
868	{error,Reason} ->
869	    ct:fail(Reason);
870	{ok,Node} ->
871	    ct:log("Node ~p started~n", [Node]),
872	    Node
873    end.
874
875
876%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
877
878
879
880open1(Config) when is_list(Config) ->
881    RootDir = proplists:get_value(priv_dir,Config),
882    NewDir = filename:join(RootDir,
883			   atom_to_list(?MODULE)
884			   ++"_files"),
885    ok = ?FILE_MODULE:make_dir(NewDir),
886    Name = filename:join(NewDir, "foo1.fil"),
887    {ok,Fd1} = ?FILE_MODULE:open(Name,read_write),
888    {ok,Fd2} = ?FILE_MODULE:open(Name,read),
889    Str = "{a,tuple}.\n",
890    io:format(Fd1,Str,[]),
891    {ok,0} = ?FILE_MODULE:position(Fd1,bof),
892    Str = io:get_line(Fd1,''),
893    Str = io:get_line(Fd2,''),
894    ok = ?FILE_MODULE:close(Fd2),
895    {ok,0} = ?FILE_MODULE:position(Fd1,bof),
896    ok = ?FILE_MODULE:truncate(Fd1),
897    eof = io:get_line(Fd1,''),
898    ok = ?FILE_MODULE:close(Fd1),
899    {ok,Fd3} = ?FILE_MODULE:open(Name,read),
900    eof = io:get_line(Fd3,''),
901    ok = ?FILE_MODULE:close(Fd3),
902    [] = flush(),
903    ok.
904
905%% Tests all open modes.
906
907old_modes(Config) when is_list(Config) ->
908    RootDir = proplists:get_value(priv_dir, Config),
909    NewDir = filename:join(RootDir,
910			   atom_to_list(?MODULE)
911			   ++"_old_open_modes"),
912    ok = ?FILE_MODULE:make_dir(NewDir),
913    Name1 = filename:join(NewDir, "foo1.fil"),
914    Marker = "hello, world",
915
916    %% write
917    {ok, Fd1} = ?FILE_MODULE:open(Name1, write),
918    ok = io:write(Fd1, Marker),
919    ok = io:put_chars(Fd1, ".\n"),
920    ok = ?FILE_MODULE:close(Fd1),
921
922    %% read
923    {ok, Fd2} = ?FILE_MODULE:open(Name1, read),
924    {ok, Marker} = io:read(Fd2, prompt),
925    ok = ?FILE_MODULE:close(Fd2),
926
927    %% read_write
928    {ok, Fd3} = ?FILE_MODULE:open(Name1, read_write),
929    {ok, Marker} = io:read(Fd3, prompt),
930    ok = io:write(Fd3, Marker),
931    ok = ?FILE_MODULE:close(Fd3),
932
933    [] = flush(),
934    ok.
935
936
937new_modes(Config) when is_list(Config) ->
938    RootDir = proplists:get_value(priv_dir, Config),
939    NewDir = filename:join(RootDir,
940			   atom_to_list(?MODULE)
941			   ++"_new_open_modes"),
942    ok = ?FILE_MODULE:make_dir(NewDir),
943    Name1 = filename:join(NewDir, "foo1.fil"),
944    Marker = "hello, world",
945
946    %% write
947    {ok, Fd1} = ?FILE_MODULE:open(Name1, [write]),
948    ok = io:write(Fd1, Marker),
949    ok = io:put_chars(Fd1, ".\n"),
950    ok = ?FILE_MODULE:close(Fd1),
951
952    %% read
953    {ok, Fd2} = ?FILE_MODULE:open(Name1, [read]),
954    {ok, Marker} = io:read(Fd2, prompt),
955    ok = ?FILE_MODULE:close(Fd2),
956
957    %% read and write
958    {ok, Fd3} = ?FILE_MODULE:open(Name1, [read, write]),
959    {ok, Marker} = io:read(Fd3, prompt),
960    ok = io:write(Fd3, Marker),
961    ok = ?FILE_MODULE:close(Fd3),
962
963    %% read by default
964    {ok, Fd4} = ?FILE_MODULE:open(Name1, []),
965    {ok, Marker} = io:read(Fd4, prompt),
966    ok = ?FILE_MODULE:close(Fd4),
967
968    %% read and binary
969    {ok, Fd5} = ?FILE_MODULE:open(Name1, [read, binary]),
970    {ok, Marker} = io:read(Fd5, prompt),
971    ok = ?FILE_MODULE:close(Fd5),
972
973    %% read, raw
974    {ok, Fd6} = ?FILE_MODULE:open(Name1, [read, raw]),
975    {ok, [$\[]} = ?FILE_MODULE:read(Fd6, 1),
976     ok = ?FILE_MODULE:close(Fd6),
977
978     %% write and sync
979     case ?FILE_MODULE:open(Name1, [write, sync]) of
980	 {ok, Fd7} ->
981	     ok = io:write(Fd7, Marker),
982	     ok = io:put_chars(Fd7, ".\n"),
983	     ok = ?FILE_MODULE:close(Fd7),
984	     {ok, Fd8} = ?FILE_MODULE:open(Name1, [read]),
985	     {ok, Marker} = io:read(Fd8, prompt),
986	     ok = ?FILE_MODULE:close(Fd8);
987	 {error, enotsup} ->
988	     %% for platforms that don't support the sync option
989	     ok
990     end,
991
992     [] = flush(),
993     ok.
994
995path_open(Config) when is_list(Config) ->
996    RootDir = proplists:get_value(priv_dir,Config),
997    NewDir = filename:join(RootDir,
998			   atom_to_list(?MODULE)
999			   ++"_path_open"),
1000    ok = ?FILE_MODULE:make_dir(NewDir),
1001    FileName = "path_open.fil",
1002    Name = filename:join(RootDir, FileName),
1003    {ok,Fd1,_FullName1} =
1004	?FILE_MODULE:path_open(
1005	   [RootDir,
1006	    "nosuch1",
1007	    NewDir],FileName,write),
1008    io:format(Fd1,"ABCDEFGH",[]),
1009    ok = ?FILE_MODULE:close(Fd1),
1010
1011    %% locate it in the last dir
1012    {ok,Fd2,_FullName2} =
1013	?FILE_MODULE:path_open(
1014	   ["nosuch1",
1015	    NewDir,
1016	    RootDir],FileName,read),
1017    {ok,2} =
1018	?FILE_MODULE:position(Fd2,2), "C" = io:get_chars(Fd2,'',1),
1019    ok = ?FILE_MODULE:close(Fd2),
1020    %% Try a failing path
1021    {error, enoent} = ?FILE_MODULE:path_open(
1022			 ["nosuch1",
1023			  NewDir],FileName,read),
1024    %% Check that it's found regardless of path, if an absolute name given
1025    {ok,Fd3,_FullPath3} =
1026	?FILE_MODULE:path_open(
1027	   ["nosuch1",
1028	    NewDir],Name,read),
1029    {ok,2} =
1030	?FILE_MODULE:position(Fd3,2), "C" = io:get_chars(Fd3,'',1),
1031    ok = ?FILE_MODULE:close(Fd3),
1032
1033    [] = flush(),
1034    ok.
1035
1036close(Config) when is_list(Config) ->
1037    RootDir = proplists:get_value(priv_dir,Config),
1038    Name = filename:join(RootDir,
1039			 atom_to_list(?MODULE)
1040			 ++"_close.fil"),
1041    {ok,Fd1} = ?FILE_MODULE:open(Name,read_write),
1042    %% Just closing it is no fun, we did that a million times already
1043    %% This is a common error, for code written before Erlang 4.3
1044    %% bacause then ?FILE_MODULE:open just returned a Pid, and not everyone
1045    %% really checked what they got.
1046    {'EXIT',_Msg} = (catch ok = ?FILE_MODULE:close({ok,Fd1})),
1047    ok = ?FILE_MODULE:close(Fd1),
1048
1049    %% Try closing one more time
1050    Val = ?FILE_MODULE:close(Fd1),
1051    io:format("Second close gave: ~p",[Val]),
1052
1053    %% All operations on a closed raw file should EINVAL, even if they're not
1054    %% supported on the current platform.
1055    {ok,Fd2} = ?FILE_MODULE:open(Name, [read, write, raw]),
1056    ok = ?FILE_MODULE:close(Fd2),
1057
1058    {error, einval} = ?FILE_MODULE:advise(Fd2, 5, 5, normal),
1059    {error, einval} = ?FILE_MODULE:allocate(Fd2, 5, 5),
1060    {error, einval} = ?FILE_MODULE:close(Fd2),
1061    {error, einval} = ?FILE_MODULE:datasync(Fd2),
1062    {error, einval} = ?FILE_MODULE:position(Fd2, 5),
1063    {error, einval} = ?FILE_MODULE:pread(Fd2, 5, 1),
1064    {error, einval} = ?FILE_MODULE:pwrite(Fd2, 5, "einval please"),
1065    {error, einval} = ?FILE_MODULE:read(Fd2, 1),
1066    {error, einval} = ?FILE_MODULE:sync(Fd2),
1067    {error, einval} = ?FILE_MODULE:truncate(Fd2),
1068    {error, einval} = ?FILE_MODULE:write(Fd2, "einval please"),
1069
1070    [] = flush(),
1071    ok.
1072
1073access(Config) when is_list(Config) ->
1074    RootDir = proplists:get_value(priv_dir,Config),
1075    Name = filename:join(RootDir,
1076			 atom_to_list(?MODULE)
1077			 ++"_access.fil"),
1078    Str = "ABCDEFGH",
1079    {ok,Fd1} = ?FILE_MODULE:open(Name,write),
1080    io:format(Fd1,Str,[]),
1081    ok = ?FILE_MODULE:close(Fd1),
1082    %% Check that we can't write when in read only mode
1083    {ok,Fd2} = ?FILE_MODULE:open(Name,read),
1084    case catch io:format(Fd2,"XXXX",[]) of
1085	ok ->
1086	    ct:fail({format,write});
1087	_ ->
1088	    ok
1089    end,
1090    ok = ?FILE_MODULE:close(Fd2),
1091    {ok,Fd3} = ?FILE_MODULE:open(Name,read),
1092    Str = io:get_line(Fd3,''),
1093    ok = ?FILE_MODULE:close(Fd3),
1094
1095    [] = flush(),
1096    ok.
1097
1098%% Tests ?FILE_MODULE:read/2 and ?FILE_MODULE:write/2.
1099
1100read_write(Config) when is_list(Config) ->
1101    RootDir = proplists:get_value(priv_dir, Config),
1102    NewDir = filename:join(RootDir,
1103			   atom_to_list(?MODULE)
1104			   ++"_read_write"),
1105    ok = ?FILE_MODULE:make_dir(NewDir),
1106    Marker = "hello, world",
1107    MarkerB = list_to_binary(Marker),
1108
1109    %% Plain file.
1110    Name1 = filename:join(NewDir, "plain.fil"),
1111    {ok, Fd1} = ?FILE_MODULE:open(Name1, [read, write]),
1112    read_write_test(Fd1, Marker, []),
1113
1114    %% Raw file.
1115    Name2 = filename:join(NewDir, "raw.fil"),
1116    {ok, Fd2} = ?FILE_MODULE:open(Name2, [read, write, raw]),
1117    read_write_test(Fd2, Marker, []),
1118
1119    %% Plain binary file.
1120    Name3 = filename:join(NewDir, "plain-b.fil"),
1121    {ok, Fd3} = ?FILE_MODULE:open(Name3, [read, write, binary]),
1122    read_write_test(Fd3, MarkerB, <<>>),
1123
1124    %% Raw binary file.
1125    Name4 = filename:join(NewDir, "raw-b.fil"),
1126    {ok, Fd4} = ?FILE_MODULE:open(Name4, [read, write, raw, binary]),
1127    read_write_test(Fd4, MarkerB, <<>>),
1128
1129    ok.
1130
1131read_write_test(File, Marker, Empty) ->
1132    ok = ?FILE_MODULE:write(File, Marker),
1133    {ok, 0} = ?FILE_MODULE:position(File, 0),
1134    {ok, Empty} = ?FILE_MODULE:read(File, 0),
1135    {ok, Marker} = ?FILE_MODULE:read(File, 100),
1136    eof = ?FILE_MODULE:read(File, 100),
1137    {ok, Empty} = ?FILE_MODULE:read(File, 0),
1138    ok = ?FILE_MODULE:close(File),
1139    [] = flush(),
1140    ok.
1141
1142
1143%% Tests ?FILE_MODULE:pread/2 and ?FILE_MODULE:pwrite/2.
1144
1145pread_write(Config) when is_list(Config) ->
1146    RootDir = proplists:get_value(priv_dir, Config),
1147    NewDir = filename:join(RootDir,
1148			   atom_to_list(?MODULE)
1149			   ++"_pread_write"),
1150    ok = ?FILE_MODULE:make_dir(NewDir),
1151    List = "hello, world",
1152    Bin = list_to_binary(List),
1153
1154    %% Plain file.
1155    Name1 = filename:join(NewDir, "plain.fil"),
1156    {ok, Fd1} = ?FILE_MODULE:open(Name1, [read, write]),
1157    pread_write_test(Fd1, List),
1158
1159    %% Raw file.
1160    Name2 = filename:join(NewDir, "raw.fil"),
1161    {ok, Fd2} = ?FILE_MODULE:open(Name2, [read, write, raw]),
1162    pread_write_test(Fd2, List),
1163
1164    %% Plain file. Binary mode.
1165    Name3 = filename:join(NewDir, "plain-binary.fil"),
1166    {ok, Fd3} = ?FILE_MODULE:open(Name3, [binary, read, write]),
1167    pread_write_test(Fd3, Bin),
1168
1169    %% Raw file. Binary mode.
1170    Name4 = filename:join(NewDir, "raw-binary.fil"),
1171    {ok, Fd4} = ?FILE_MODULE:open(Name4, [binary, read, write, raw]),
1172    pread_write_test(Fd4, Bin),
1173
1174    ok.
1175
1176pread_write_test(File, Data) ->
1177    io:format("~p:pread_write_test(~p,~p)~n", [?MODULE, File, Data]),
1178    Size = if is_binary(Data) -> byte_size(Data);
1179	      is_list(Data) -> length(Data)
1180	   end,
1181    I = Size + 17,
1182    ok = ?FILE_MODULE:pwrite(File, 0, Data),
1183    {ok, Data} = ?FILE_MODULE:pread(File, 0, I),
1184    {ok, [Data]} = ?FILE_MODULE:pread(File, [{0, I}]),
1185    eof = ?FILE_MODULE:pread(File, I, 1),
1186    ok = ?FILE_MODULE:pwrite(File, [{0, Data}, {I, Data}]),
1187    {ok, [Data, eof, Data]} =
1188	?FILE_MODULE:pread(File, [{0, Size}, {2*I, 1}, {I, Size}]),
1189    Plist = lists:seq(21*I, 0, -I),
1190    Pwrite = lists:map(fun(P)->{P,Data}end, Plist),
1191    Pread = [{22*I,Size} | lists:map(fun(P)->{P,Size}end, Plist)],
1192    Presult = [eof | lists:map(fun(_)->Data end, Plist)],
1193    ok = ?FILE_MODULE:pwrite(File, Pwrite),
1194    {ok, Presult} = ?FILE_MODULE:pread(File, Pread),
1195    ok = ?FILE_MODULE:close(File),
1196    [] = flush(),
1197    ok.
1198
1199%% Test appending to a file.
1200append(Config) when is_list(Config) ->
1201    RootDir = proplists:get_value(priv_dir, Config),
1202    NewDir = filename:join(RootDir,
1203			   atom_to_list(?MODULE)
1204			   ++"_append"),
1205    ok = ?FILE_MODULE:make_dir(NewDir),
1206
1207    First = "First line\n",
1208    Second = "Seond lines comes here\n",
1209    Third = "And here is the third line\n",
1210
1211    %% Write a small text file.
1212    Name1 = filename:join(NewDir, "a_file.txt"),
1213    {ok, Fd1} = ?FILE_MODULE:open(Name1, [write]),
1214    ok = io:format(Fd1, First, []),
1215    ok = io:format(Fd1, Second, []),
1216    ok = ?FILE_MODULE:close(Fd1),
1217
1218    %% Open it a again and a append a line to it.
1219    {ok, Fd2} = ?FILE_MODULE:open(Name1, [append]),
1220    ok = io:format(Fd2, Third, []),
1221    ok = ?FILE_MODULE:close(Fd2),
1222
1223    %% Read it back and verify.
1224    Expected = list_to_binary([First, Second, Third]),
1225    {ok, Expected} = ?FILE_MODULE:read_file(Name1),
1226
1227    [] = flush(),
1228    ok.
1229
1230open_errors(Config) when is_list(Config) ->
1231    DataDir =
1232	filename:dirname(
1233	  filename:join(proplists:get_value(data_dir, Config), "x")),
1234    DataDirSlash = DataDir++"/",
1235    {error, E1} = ?FILE_MODULE:open(DataDir, [read]),
1236    {error, E2} = ?FILE_MODULE:open(DataDirSlash, [read]),
1237    {error, E3} = ?FILE_MODULE:open(DataDir, [write]),
1238    {error, E4} = ?FILE_MODULE:open(DataDirSlash, [write]),
1239    {eisdir,eisdir,eisdir,eisdir} = {E1,E2,E3,E4},
1240
1241    [] = flush(),
1242    ok.
1243
1244%% Test exclusive access to a file.
1245exclusive(Config) when is_list(Config) ->
1246    RootDir = proplists:get_value(priv_dir,Config),
1247    NewDir = filename:join(RootDir,
1248			   atom_to_list(?MODULE)
1249			   ++"_exclusive"),
1250    ok = ?FILE_MODULE:make_dir(NewDir),
1251    Name = filename:join(NewDir, "ex_file.txt"),
1252    {ok, Fd} = ?FILE_MODULE:open(Name, [write, exclusive]),
1253    {error, eexist} = ?FILE_MODULE:open(Name, [write, exclusive]),
1254    ok = ?FILE_MODULE:close(Fd),
1255    ok.
1256
1257%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1258
1259
1260pos1(Config) when is_list(Config) ->
1261    RootDir = proplists:get_value(priv_dir,Config),
1262    Name = filename:join(RootDir,
1263			 atom_to_list(?MODULE)
1264			 ++"_pos1.fil"),
1265    {ok,Fd1} = ?FILE_MODULE:open(Name,write),
1266    io:format(Fd1,"ABCDEFGH",[]),
1267    ok = ?FILE_MODULE:close(Fd1),
1268    {ok,Fd2} = ?FILE_MODULE:open(Name,read),
1269
1270    %% Start pos is first char
1271    io:format("Relative positions"),
1272    "A" = io:get_chars(Fd2,'',1),
1273    {ok,2} = ?FILE_MODULE:position(Fd2,{cur,1}),
1274    "C" = io:get_chars(Fd2,'',1),
1275    {ok,0} = ?FILE_MODULE:position(Fd2,{cur,-3}),
1276    "A" = io:get_chars(Fd2,'',1),
1277    %% Backwards from first char should be an error
1278    {ok,0} = ?FILE_MODULE:position(Fd2,{cur,-1}),
1279    {error, einval} = ?FILE_MODULE:position(Fd2,{cur,-1}),
1280    %% Reset position and move again
1281    {ok,0} = ?FILE_MODULE:position(Fd2,0),
1282    {ok,2} = ?FILE_MODULE:position(Fd2,{cur,2}),
1283    "C" = io:get_chars(Fd2,'',1),
1284    %% Go a lot forwards
1285    {ok,13} = ?FILE_MODULE:position(Fd2,{cur,10}),
1286    eof = io:get_chars(Fd2,'',1),
1287
1288    %% Try some fixed positions
1289    io:format("Fixed positions"),
1290    {ok,8} =
1291	?FILE_MODULE:position(Fd2,8), eof = io:get_chars(Fd2,'',1),
1292    {ok,8} =
1293	?FILE_MODULE:position(Fd2,cur), eof = io:get_chars(Fd2,'',1),
1294    {ok,7} =
1295	?FILE_MODULE:position(Fd2,7), "H" = io:get_chars(Fd2,'',1),
1296    {ok,0} =
1297	?FILE_MODULE:position(Fd2,0), "A" = io:get_chars(Fd2,'',1),
1298    {ok,3} =
1299	?FILE_MODULE:position(Fd2,3), "D" = io:get_chars(Fd2,'',1),
1300    {ok,12} =
1301	?FILE_MODULE:position(Fd2,12), eof = io:get_chars(Fd2,'',1),
1302    {ok,3} =
1303	?FILE_MODULE:position(Fd2,3), "D" = io:get_chars(Fd2,'',1),
1304    %% Try the {bof,X} notation
1305    {ok,3} = ?FILE_MODULE:position(Fd2,{bof,3}),
1306    "D" = io:get_chars(Fd2,'',1),
1307
1308    %% Try eof positions
1309    io:format("EOF positions"),
1310    {ok,8} =
1311	?FILE_MODULE:position(Fd2,{eof,0}), eof=io:get_chars(Fd2,'',1),
1312    {ok,7} =
1313	?FILE_MODULE:position(Fd2,{eof,-1}),
1314    "H" = io:get_chars(Fd2,'',1),
1315    {ok,0} =
1316	?FILE_MODULE:position(Fd2,{eof,-8}), "A"=io:get_chars(Fd2,'',1),
1317    {error, einval} = ?FILE_MODULE:position(Fd2,{eof,-9}),
1318
1319    [] = flush(),
1320    ok.
1321
1322pos2(Config) when is_list(Config) ->
1323    RootDir = proplists:get_value(priv_dir,Config),
1324    Name = filename:join(RootDir,
1325			 atom_to_list(?MODULE)
1326			 ++"_pos2.fil"),
1327    {ok,Fd1} = ?FILE_MODULE:open(Name,write),
1328    io:format(Fd1,"ABCDEFGH",[]),
1329    ok = ?FILE_MODULE:close(Fd1),
1330    {ok,Fd2} = ?FILE_MODULE:open(Name,read),
1331    {error, einval} = ?FILE_MODULE:position(Fd2,-1),
1332
1333    %% Make sure that we still can search after an error.
1334    {ok,0} = ?FILE_MODULE:position(Fd2, 0),
1335    {ok,3} = ?FILE_MODULE:position(Fd2, {bof,3}),
1336    "D" = io:get_chars(Fd2,'',1),
1337
1338    [] = flush(),
1339    io:format("DONE"),
1340    ok.
1341
1342%% When it does not use raw mode, file:position had a bug.
1343pos3(Config) when is_list(Config) ->
1344    RootDir = proplists:get_value(data_dir, Config),
1345    Name = filename:join(RootDir, "realmen.html.gz"),
1346
1347    {ok, Fd} = ?FILE_MODULE:open(Name, [read, binary]),
1348    {ok, _}  = ?FILE_MODULE:read(Fd, 5),
1349    {error, einval} = ?FILE_MODULE:position(Fd, {bof, -1}),
1350
1351    %% Here ok had returned =(
1352    {error, einval} = ?FILE_MODULE:position(Fd, {cur, -10}),
1353    %% That test is actually questionable since file:position/2
1354    %% is documented to leave the file position undefined after
1355    %% it has returned an error.  But on Posix systems the position
1356    %% is guaranteed to be unchanged after an error return.  On e.g
1357    %% Windows there is nothing stated about this in the documentation.
1358
1359    ok.
1360
1361file_info_basic_file(Config) when is_list(Config) ->
1362    RootDir = proplists:get_value(priv_dir, Config),
1363
1364    %% Create a short file.
1365    Name = filename:join(RootDir,
1366			 atom_to_list(?MODULE)
1367			 ++"_basic_test.fil"),
1368    {ok,Fd1} = ?FILE_MODULE:open(Name, write),
1369    io:put_chars(Fd1, "foo bar"),
1370    ok = ?FILE_MODULE:close(Fd1),
1371
1372    %% Don't crash the file server when passing incorrect arguments.
1373    {error,badarg} = ?FILE_MODULE:read_file_info(Name, [{time, gurka}]),
1374    {error,badarg} = ?FILE_MODULE:read_file_info([#{} | gaffel]),
1375
1376    %% Test that the file has the expected attributes.
1377    %% The times are tricky, so we will save them to a separate test case.
1378    {ok,FileInfo} = ?FILE_MODULE:read_file_info(Name),
1379    {ok,FileInfo} = ?FILE_MODULE:read_file_info(Name, [raw]),
1380    #file_info{size=Size,type=Type,access=Access,
1381	       atime=AccessTime,mtime=ModifyTime} = FileInfo,
1382    io:format("Access ~p, Modify ~p", [AccessTime, ModifyTime]),
1383    Size = 7,
1384    Type = regular,
1385    read_write = Access,
1386    true = abs(time_dist(filter_atime(AccessTime, Config),
1387			 filter_atime(ModifyTime,
1388				      Config))) < 2,
1389    all_integers(tuple_to_list(AccessTime) ++ tuple_to_list(ModifyTime)),
1390
1391    [] = flush(),
1392    ok.
1393
1394file_info_basic_directory(Config) when is_list(Config) ->
1395    %% Note: filename:join/1 removes any trailing slash,
1396    %% which is essential for ?FILE_MODULE:file_info/1 to work on
1397    %% platforms such as Windows95.
1398    RootDir = filename:join([proplists:get_value(priv_dir, Config)]),
1399
1400    %% Test that the RootDir directory has the expected attributes.
1401    test_directory(RootDir, read_write),
1402
1403    %% Note that on Windows file systems,
1404    %% "/" or "c:/" are *NOT* directories.
1405    %% Therefore, test that ?FILE_MODULE:file_info/1 behaves as if they were
1406    %% directories.
1407    case os:type() of
1408        {win32, _} ->
1409            test_directory("/", read_write),
1410            test_directory("c:/", read_write),
1411            test_directory("c:\\", read_write);
1412        _ ->
1413            test_directory("/", read)
1414    end,
1415    ok.
1416
1417test_directory(Name, ExpectedAccess) ->
1418    {ok,FileInfo} = ?FILE_MODULE:read_file_info(Name),
1419    {ok,FileInfo} = ?FILE_MODULE:read_file_info(Name, [raw]),
1420    #file_info{size=Size,type=Type,access=Access,
1421               atime=AccessTime,mtime=ModifyTime} = FileInfo,
1422    io:format("Testing directory ~s", [Name]),
1423    io:format("Directory size is ~p", [Size]),
1424    io:format("Access ~p", [Access]),
1425    io:format("Access time ~p; Modify time~p",
1426	      [AccessTime, ModifyTime]),
1427    Type = directory,
1428    Access = ExpectedAccess,
1429    all_integers(tuple_to_list(AccessTime) ++ tuple_to_list(ModifyTime)),
1430    [] = flush(),
1431    ok.
1432
1433all_integers([{A,B,C}|T]) ->
1434    all_integers([A,B,C|T]);
1435all_integers([Int|Rest]) when is_integer(Int) ->
1436    all_integers(Rest);
1437all_integers([]) -> ok.
1438
1439%% Try something nonexistent.
1440
1441file_info_bad(Config) when is_list(Config) ->
1442    RootDir = filename:join([proplists:get_value(priv_dir, Config)]),
1443    FileName = filename:join(RootDir, atom_to_list(?MODULE) ++ "_nonexistent"),
1444    {error,enoent} = ?FILE_MODULE:read_file_info(FileName),
1445    {error,enoent} = ?FILE_MODULE:read_file_info(FileName, [raw]),
1446    {error, enoent} = ?FILE_MODULE:read_file_info(""),
1447    {error, enoent} = ?FILE_MODULE:read_file_info("", [raw]),
1448    [] = flush(),
1449    ok.
1450
1451%% Test that the file times behave as they should.
1452
1453file_info_times(Config) when is_list(Config) ->
1454    %% We have to try this twice, since if the test runs across the change
1455    %% of a month the time diff calculations will fail. But it won't happen
1456    %% if you run it twice in succession.
1457    test_server:m_out_of_n(
1458      1,2,
1459      fun() -> file_info_int(Config) end),
1460    ok.
1461
1462file_info_int(Config) ->
1463    %% Note: filename:join/1 removes any trailing slash,
1464    %% which is essential for ?FILE_MODULE:file_info/1 to work on
1465    %% platforms such as Windows95.
1466
1467    RootDir = filename:join([proplists:get_value(priv_dir, Config)]),
1468    io:format("RootDir = ~p", [RootDir]),
1469
1470    Name = filename:join(RootDir,
1471			 atom_to_list(?MODULE)
1472			 ++"_file_info.fil"),
1473    {ok,Fd1} = ?FILE_MODULE:open(Name,write),
1474    io:put_chars(Fd1,"foo"),
1475
1476    %% check that the file got a modify date max a few seconds away from now
1477    {ok,FileInfo1} = ?FILE_MODULE:read_file_info(Name),
1478    {ok,FileInfo1Raw} = ?FILE_MODULE:read_file_info(Name, [raw]),
1479
1480    %% We assert that everything but the size is the same, on some OSs the
1481    %% size may not have been flushed to disc and we do not want to do a
1482    %% sync to force it.
1483    FileInfo1Raw = FileInfo1#file_info{ size = FileInfo1Raw#file_info.size },
1484
1485    #file_info{type=regular,atime=AccTime1,mtime=ModTime1} = FileInfo1,
1486
1487    Now = erlang:localtime(), %???
1488    io:format("Now ~p",[Now]),
1489    io:format("Open file Acc ~p Mod ~p",[AccTime1,ModTime1]),
1490    true = abs(time_dist(filter_atime(Now, Config),
1491			 filter_atime(AccTime1,
1492				      Config))) < 8,
1493    true = abs(time_dist(Now,ModTime1)) < 8,
1494
1495    %% Sleep until we can be sure the seconds value has changed.
1496    %% Note: FAT-based filesystem (like on Windows 95) have
1497    %% a resolution of 2 seconds.
1498    timer:sleep(2200),
1499
1500    %% close the file, and watch the modify date change
1501    ok = ?FILE_MODULE:close(Fd1),
1502    {ok,FileInfo2} = ?FILE_MODULE:read_file_info(Name),
1503    {ok,FileInfo2} = ?FILE_MODULE:read_file_info(Name, [raw]),
1504    #file_info{size=Size,type=regular,access=Access,
1505               atime=AccTime2,mtime=ModTime2} = FileInfo2,
1506    io:format("Closed file Acc ~p Mod ~p",[AccTime2,ModTime2]),
1507    true = time_dist(ModTime1,ModTime2) >= 0,
1508
1509    %% this file is supposed to be binary, so it'd better keep it's size
1510    Size = 3,
1511    Access = read_write,
1512
1513    %% Do some directory checking
1514    {ok,FileInfo3} = ?FILE_MODULE:read_file_info(RootDir),
1515    {ok,FileInfo3} = ?FILE_MODULE:read_file_info(RootDir, [raw]),
1516    #file_info{size=DSize,type=directory,access=DAccess,
1517               atime=AccTime3,mtime=ModTime3} = FileInfo3,
1518    %% this dir was modified only a few secs ago
1519    io:format("Dir Acc ~p; Mod ~p; Now ~p", [AccTime3, ModTime3, Now]),
1520    true = abs(time_dist(Now,ModTime3)) < 5,
1521    DAccess = read_write,
1522    io:format("Dir size is ~p",[DSize]),
1523
1524    [] = flush(),
1525    ok.
1526
1527%% Filter access times, to copy with a deficiency of FAT file systems
1528%% (on Windows): The access time is actually only a date.
1529
1530filter_atime(Atime, Config) ->
1531    case lists:member(no_access_time, Config) of
1532	true ->
1533	    case Atime of
1534	    	{Date, _} ->
1535		    {Date, {0, 0, 0}};
1536		{Y, M, D, _, _, _} ->
1537		    {Y, M, D, 0, 0, 0}
1538	    end;
1539	false ->
1540	    Atime
1541    end.
1542
1543%% Test the write_file_info/2 function.
1544
1545file_write_file_info(Config) when is_list(Config) ->
1546    RootDir = get_good_directory(Config),
1547    io:format("RootDir = ~p", [RootDir]),
1548
1549    %% Set the file to read only AND update the file times at the same time.
1550    %% (This used to fail on Windows NT/95 for a local filesystem.)
1551    %% Note: Seconds must be even; see note in file_info_times/1.
1552
1553    Name1 = filename:join(RootDir,
1554			  atom_to_list(?MODULE)
1555			  ++"_write_file_info_ro"),
1556    ok = ?FILE_MODULE:write_file(Name1, "hello"),
1557    Time = {{1997, 01, 02}, {12, 35, 42}},
1558    Info = #file_info{mode=8#400, atime=Time, mtime=Time, ctime=Time},
1559    ok = ?FILE_MODULE:write_file_info(Name1, Info),
1560
1561    %% Read back the times.
1562
1563    {ok, ActualInfo} = ?FILE_MODULE:read_file_info(Name1),
1564    #file_info{mode=_Mode, atime=ActAtime, mtime=Time,
1565	       ctime=ActCtime} = ActualInfo,
1566    FilteredAtime = filter_atime(Time, Config),
1567    FilteredAtime = filter_atime(ActAtime, Config),
1568    case os:type() of
1569	{win32, _} ->
1570	    %% On Windows, "ctime" means creation time and it can
1571	    %% be set.
1572	    ActCtime = Time;
1573	_ ->
1574	    ok
1575    end,
1576    {error, eacces} = ?FILE_MODULE:write_file(Name1, "hello again"),
1577
1578    %% Make the file writable again.
1579
1580    ?FILE_MODULE:write_file_info(Name1, #file_info{mode=8#600}),
1581    ok = ?FILE_MODULE:write_file(Name1, "hello again"),
1582
1583    %% And unwritable.
1584    ?FILE_MODULE:write_file_info(Name1, #file_info{mode=8#400}),
1585    {error, eacces} = ?FILE_MODULE:write_file(Name1, "hello again"),
1586
1587    %% Same with raw.
1588    ?FILE_MODULE:write_file_info(Name1, #file_info{mode=8#600}, [raw]),
1589    ok = ?FILE_MODULE:write_file(Name1, "hello again"),
1590    ?FILE_MODULE:write_file_info(Name1, #file_info{mode=8#400}, [raw]),
1591    {error,eacces} = ?FILE_MODULE:write_file(Name1, "hello again"),
1592
1593    %% Write the times again.
1594    %% Note: Seconds must be even; see note in file_info_times/1.
1595
1596    NewTime = {{1997, 02, 15}, {13, 18, 20}},
1597    NewInfo = #file_info{atime=NewTime, mtime=NewTime, ctime=NewTime},
1598    ok = ?FILE_MODULE:write_file_info(Name1, NewInfo),
1599    {ok, ActualInfo2} = ?FILE_MODULE:read_file_info(Name1),
1600    #file_info{atime=NewActAtime, mtime=NewTime,
1601	       ctime=NewActCtime} = ActualInfo2,
1602    NewFilteredAtime = filter_atime(NewTime, Config),
1603    NewFilteredAtime = filter_atime(NewActAtime, Config),
1604    case os:type() of
1605	{win32, _} -> NewActCtime = NewTime;
1606	_ -> ok
1607    end,
1608
1609    %% The file should still be unwritable.
1610    {error, eacces} = ?FILE_MODULE:write_file(Name1, "hello again"),
1611
1612    %% Make the file writeable again, so that we can remove the
1613    %% test suites ... :-)
1614    ?FILE_MODULE:write_file_info(Name1, #file_info{mode=8#600}),
1615
1616    [] = flush(),
1617    ok.
1618
1619file_wfi_helpers(Config) when is_list(Config) ->
1620    RootDir = get_good_directory(Config),
1621    io:format("RootDir = ~p", [RootDir]),
1622
1623    Name = filename:join(RootDir,
1624                         atom_to_list(?MODULE) ++ "_wfi_helpers"),
1625
1626    ok = ?FILE_MODULE:write_file(Name, "hello again"),
1627    NewTime = {{1997, 02, 15}, {13, 18, 20}},
1628    ok = ?FILE_MODULE:change_time(Name, NewTime, NewTime),
1629
1630    {ok, #file_info{atime=NewActAtime, mtime=NewTime}} =
1631        ?FILE_MODULE:read_file_info(Name),
1632
1633    NewFilteredAtime = filter_atime(NewTime, Config),
1634    NewFilteredAtime = filter_atime(NewActAtime, Config),
1635
1636    %% Make the file unwritable
1637    ok = ?FILE_MODULE:change_mode(Name, 8#400),
1638    {error, eacces} = ?FILE_MODULE:write_file(Name, "hello again"),
1639
1640    %% ... and writable again
1641    ok = ?FILE_MODULE:change_mode(Name, 8#600),
1642    ok = ?FILE_MODULE:write_file(Name, "hello again"),
1643
1644    %% We have no idea which users will work, so all we can do is to check
1645    %% that it returns enoent instead of crashing.
1646    {error, enoent} = ?FILE_MODULE:change_group("bogus file name", 0),
1647    {error, enoent} = ?FILE_MODULE:change_owner("bogus file name", 0),
1648
1649    [] = flush(),
1650    ok.
1651
1652%% Returns a directory on a file system that has correct file times.
1653
1654get_good_directory(Config) ->
1655    proplists:get_value(priv_dir, Config).
1656
1657
1658consult1(Config) when is_list(Config) ->
1659    RootDir = proplists:get_value(priv_dir,Config),
1660    Name = filename:join(RootDir,
1661			 atom_to_list(?MODULE)
1662			 ++"_consult.fil"),
1663    {ok,Fd1} = ?FILE_MODULE:open(Name,write),
1664    %% note that there is no final \n (only a space)
1665    io:format(Fd1,
1666	      "{this,[is,1.0],'journey'}.\n\"into\". (sound). ",
1667	      []),
1668    ok = ?FILE_MODULE:close(Fd1),
1669    {ok,[{this,[is,1.0],journey},"into",sound]} =
1670	?FILE_MODULE:consult(Name),
1671
1672    {ok,Fd2} = ?FILE_MODULE:open(Name,write),
1673    %% note the missing double quote
1674    io:format(
1675      Fd2,"{this,[is,1.0],'journey'}.\n \"into. (sound). ",[]),
1676    ok = ?FILE_MODULE:close(Fd2),
1677    {error, {_, _, _} = Msg} = ?FILE_MODULE:consult(Name),
1678    io:format("Errmsg: ~p",[Msg]),
1679
1680    {error, enoent} = ?FILE_MODULE:consult(Name ++ ".nonexistent"),
1681
1682    [] = flush(),
1683    ok.
1684
1685path_consult(Config) when is_list(Config) ->
1686    RootDir = proplists:get_value(priv_dir,Config),
1687    FileName = atom_to_list(?MODULE)++"_path_consult.fil",
1688    Name = filename:join(RootDir, FileName),
1689    {ok,Fd1} = ?FILE_MODULE:open(Name,write),
1690    io:format(Fd1,"{this,is,a,journey,into,sound}.\n",[]),
1691    ok = ?FILE_MODULE:close(Fd1),
1692    %% File last in path
1693    {ok,[{this,is,a,journey,into,sound}],Dir} =
1694	?FILE_MODULE:path_consult(
1695	   [filename:join(RootDir, "dir1"),
1696	    filename:join(RootDir, ".."),
1697	    filename:join(RootDir, "dir2"),
1698	    RootDir], FileName),
1699    true = lists:prefix(RootDir,Dir),
1700
1701    %% While maybe not an error, it may be worth noting that
1702    %% when the full path to a file is given, it's always found
1703    %% regardless of the contents of the path
1704    {ok,_,_} = ?FILE_MODULE:path_consult(["nosuch1","nosuch2"],Name),
1705
1706    [] = flush(),
1707    ok.
1708
1709
1710eval1(Config) when is_list(Config) ->
1711    RootDir = proplists:get_value(priv_dir,Config),
1712    Name = filename:join(RootDir,
1713			 atom_to_list(?MODULE)++"_eval.fil"),
1714    {ok,Fd1} = ?FILE_MODULE:open(Name,write),
1715    %% note that there is no final \n (only a space)
1716    io:format(Fd1,"put(evaluated_ok,\ntrue). ",[]),
1717    ok = ?FILE_MODULE:close(Fd1),
1718    ok = ?FILE_MODULE:eval(Name),
1719    true = get(evaluated_ok),
1720
1721    {ok,Fd2} = ?FILE_MODULE:open(Name,write),
1722    %% note that there is no final \n (only a space)
1723    io:format(Fd2,"put(evaluated_ok,\nR). ",[]),
1724    ok = ?FILE_MODULE:close(Fd2),
1725    ok = ?FILE_MODULE:eval(
1726	    Name,
1727	    erl_eval:add_binding('R', true, erl_eval:new_bindings())),
1728    true = get(evaluated_ok),
1729
1730    {ok,Fd3} = ?FILE_MODULE:open(Name,write),
1731    %% garbled
1732    io:format(Fd3,"puGARBLED-GARBLED\ntrue). ",[]),
1733    ok = ?FILE_MODULE:close(Fd3),
1734    {error, {_, _, _} = Msg} = ?FILE_MODULE:eval(Name),
1735    io:format("Errmsg1: ~p",[Msg]),
1736
1737    {error, enoent} = ?FILE_MODULE:eval(Name ++ ".nonexistent"),
1738
1739    [] = flush(),
1740    ok.
1741
1742path_eval(Config) when is_list(Config) ->
1743    RootDir = proplists:get_value(priv_dir,Config),
1744    FileName = atom_to_list(?MODULE)++"_path_eval.fil",
1745    Name = filename:join(RootDir, FileName),
1746    {ok,Fd1} = ?FILE_MODULE:open(Name,write),
1747    io:format(Fd1,"put(evaluated_ok,true).\n",[]),
1748    ok = ?FILE_MODULE:close(Fd1),
1749    %% File last in path
1750    {ok,Dir} =
1751	?FILE_MODULE:path_eval(
1752	   [filename:join(RootDir, "dir1"),
1753	    filename:join(RootDir, ".."),
1754	    filename:join(RootDir, "dir2"),
1755	    RootDir],FileName),
1756    true = get(evaluated_ok),
1757    true = lists:prefix(RootDir,Dir),
1758
1759    %% While maybe not an error, it may be worth noting that
1760    %% when the full path to a file is given, it's always found
1761    %% regardless of the contents of the path
1762    {ok,Fd2} = ?FILE_MODULE:open(Name,write),
1763    io:format(Fd2,"put(evaluated_ok,R).\n",[]),
1764    ok = ?FILE_MODULE:close(Fd2),
1765    {ok,_} = ?FILE_MODULE:path_eval(
1766		["nosuch1","nosuch2"],
1767		Name,
1768		erl_eval:add_binding('R', true, erl_eval:new_bindings())),
1769
1770    [] = flush(),
1771    ok.
1772
1773
1774script1(Config) when is_list(Config) ->
1775    RootDir = proplists:get_value(priv_dir,Config),
1776    Name = filename:join(RootDir,
1777			 atom_to_list(?MODULE)++"_script.fil"),
1778    {ok,Fd1} = ?FILE_MODULE:open(Name,write),
1779    %% note that there is no final \n (only a space)
1780    io:format(Fd1,"A = 11,\nB = 6,\nA+B. ",[]),
1781    ok = ?FILE_MODULE:close(Fd1),
1782    {ok,17} = ?FILE_MODULE:script(Name),
1783
1784    {ok,Fd2} = ?FILE_MODULE:open(Name,write),
1785    %% note that there is no final \n (only a space)
1786    io:format(Fd2,"A = 11,\nA+B. ",[]),
1787    ok = ?FILE_MODULE:close(Fd2),
1788    {ok,17} = ?FILE_MODULE:script(
1789		 Name,
1790		 erl_eval:add_binding('B', 6, erl_eval:new_bindings())),
1791
1792    {ok,Fd3} = ?FILE_MODULE:open(Name,write),
1793    io:format(Fd3,"A = 11,\nB = six,\nA+B. ",[]),
1794    ok = ?FILE_MODULE:close(Fd3),
1795    {error, {_, _, _} = Msg} = ?FILE_MODULE:script(Name),
1796    io:format("Errmsg1: ~p",[Msg]),
1797
1798    {error, enoent} = ?FILE_MODULE:script(Name ++ ".nonexistent"),
1799
1800    [] = flush(),
1801    ok.
1802
1803path_script(Config) when is_list(Config) ->
1804    RootDir = proplists:get_value(priv_dir,Config),
1805    FileName = atom_to_list(?MODULE)++"_path_script.fil",
1806    Name = filename:join(RootDir, FileName),
1807    {ok,Fd1} = ?FILE_MODULE:open(Name,write),
1808    io:format(Fd1,"A = 11,\nB = 6,\nA+B.\n",[]),
1809    ok = ?FILE_MODULE:close(Fd1),
1810    %% File last in path
1811    {ok, 17, Dir} =
1812	?FILE_MODULE:path_script(
1813	   [filename:join(RootDir, "dir1"),
1814	    filename:join(RootDir, ".."),
1815	    filename:join(RootDir, "dir2"),
1816	    RootDir],FileName),
1817    true = lists:prefix(RootDir,Dir),
1818
1819    %% While maybe not an error, it may be worth noting that
1820    %% when the full path to a file is given, it's always found
1821    %% regardless of the contents of the path
1822    {ok,Fd2} = ?FILE_MODULE:open(Name,write),
1823    io:format(Fd2,"A = 11,\nA+B.",[]),
1824    ok = ?FILE_MODULE:close(Fd2),
1825    {ok, 17, Dir} =
1826	?FILE_MODULE:path_script(
1827	   ["nosuch1","nosuch2"],
1828	   Name,
1829	   erl_eval:add_binding('B', 6, erl_eval:new_bindings())),
1830
1831    [] = flush(),
1832    ok.
1833
1834
1835
1836truncate(Config) when is_list(Config) ->
1837    RootDir = proplists:get_value(priv_dir,Config),
1838    Name = filename:join(RootDir,
1839			 atom_to_list(?MODULE)
1840			 ++"_truncate.fil"),
1841
1842    %% Create a file with some data.
1843    MyData = "0123456789abcdefghijklmnopqrstuvxyz",
1844    ok = ?FILE_MODULE:write_file(Name, MyData),
1845
1846    %% Truncate the file to 10 characters.
1847    {ok, Fd} = ?FILE_MODULE:open(Name, read_write),
1848    {ok, 10} = ?FILE_MODULE:position(Fd, 10),
1849    ok = ?FILE_MODULE:truncate(Fd),
1850    ok = ?FILE_MODULE:close(Fd),
1851
1852    %% Read back the file and check that it has been truncated.
1853    Expected = list_to_binary("0123456789"),
1854    {ok, Expected} = ?FILE_MODULE:read_file(Name),
1855
1856    %% Open the file read only and verify that it is not possible to
1857    %% truncate it, OTP-1960
1858    {ok, Fd2} = ?FILE_MODULE:open(Name, read),
1859    {ok, 5} = ?FILE_MODULE:position(Fd2, 5),
1860    {error, _} = ?FILE_MODULE:truncate(Fd2),
1861
1862    [] = flush(),
1863    ok.
1864
1865
1866%% Tests that ?FILE_MODULE:datasync/1 at least doesn't crash.
1867datasync(Config) when is_list(Config) ->
1868    PrivDir = proplists:get_value(priv_dir, Config),
1869    Sync = filename:join(PrivDir,
1870			 atom_to_list(?MODULE)
1871			 ++"_sync.fil"),
1872
1873    %% Raw open.
1874    {ok, Fd} = ?FILE_MODULE:open(Sync, [write, raw]),
1875    ok = ?FILE_MODULE:datasync(Fd),
1876    ok = ?FILE_MODULE:close(Fd),
1877
1878    %% Ordinary open.
1879    {ok, Fd2} = ?FILE_MODULE:open(Sync, [write]),
1880    ok = ?FILE_MODULE:datasync(Fd2),
1881    ok = ?FILE_MODULE:close(Fd2),
1882
1883    [] = flush(),
1884    ok.
1885
1886
1887%% Tests that ?FILE_MODULE:sync/1 at least doesn't crash.
1888sync(Config) when is_list(Config) ->
1889    PrivDir = proplists:get_value(priv_dir, Config),
1890    Sync = filename:join(PrivDir,
1891			 atom_to_list(?MODULE)
1892			 ++"_sync.fil"),
1893
1894    %% Raw open.
1895    {ok, Fd} = ?FILE_MODULE:open(Sync, [write, raw]),
1896    ok = ?FILE_MODULE:sync(Fd),
1897    ok = ?FILE_MODULE:close(Fd),
1898
1899    %% Ordinary open.
1900    {ok, Fd2} = ?FILE_MODULE:open(Sync, [write]),
1901    ok = ?FILE_MODULE:sync(Fd2),
1902    ok = ?FILE_MODULE:close(Fd2),
1903
1904    [] = flush(),
1905    ok.
1906
1907%% Tests that ?FILE_MODULE:advise/4 at least doesn't crash.
1908advise(Config) when is_list(Config) ->
1909    PrivDir = proplists:get_value(priv_dir, Config),
1910    Advise = filename:join(PrivDir,
1911			   atom_to_list(?MODULE)
1912			   ++"_advise.fil"),
1913
1914    Line1 = "Hello\n",
1915    Line2 = "World!\n",
1916
1917    {ok, Fd} = ?FILE_MODULE:open(Advise, [write]),
1918    ok = ?FILE_MODULE:advise(Fd, 0, 0, normal),
1919    ok = io:format(Fd, "~s", [Line1]),
1920    ok = io:format(Fd, "~s", [Line2]),
1921    ok = ?FILE_MODULE:close(Fd),
1922
1923    {ok, Fd2} = ?FILE_MODULE:open(Advise, [write]),
1924    ok = ?FILE_MODULE:advise(Fd2, 0, 0, random),
1925    ok = io:format(Fd2, "~s", [Line1]),
1926    ok = io:format(Fd2, "~s", [Line2]),
1927    ok = ?FILE_MODULE:close(Fd2),
1928
1929    {ok, Fd3} = ?FILE_MODULE:open(Advise, [write]),
1930    ok = ?FILE_MODULE:advise(Fd3, 0, 0, sequential),
1931    ok = io:format(Fd3, "~s", [Line1]),
1932    ok = io:format(Fd3, "~s", [Line2]),
1933    ok = ?FILE_MODULE:close(Fd3),
1934
1935    {ok, Fd4} = ?FILE_MODULE:open(Advise, [write]),
1936    ok = ?FILE_MODULE:advise(Fd4, 0, 0, will_need),
1937    ok = io:format(Fd4, "~s", [Line1]),
1938    ok = io:format(Fd4, "~s", [Line2]),
1939    ok = ?FILE_MODULE:close(Fd4),
1940
1941    {ok, Fd5} = ?FILE_MODULE:open(Advise, [write]),
1942    ok = ?FILE_MODULE:advise(Fd5, 0, 0, dont_need),
1943    ok = io:format(Fd5, "~s", [Line1]),
1944    ok = io:format(Fd5, "~s", [Line2]),
1945    ok = ?FILE_MODULE:close(Fd5),
1946
1947    {ok, Fd6} = ?FILE_MODULE:open(Advise, [write]),
1948    ok = ?FILE_MODULE:advise(Fd6, 0, 0, no_reuse),
1949    ok = io:format(Fd6, "~s", [Line1]),
1950    ok = io:format(Fd6, "~s", [Line2]),
1951    ok = ?FILE_MODULE:close(Fd6),
1952
1953    {ok, Fd7} = ?FILE_MODULE:open(Advise, [write]),
1954    {error, einval} = ?FILE_MODULE:advise(Fd7, 0, 0, bad_advise),
1955    ok = ?FILE_MODULE:close(Fd7),
1956
1957    %% test write without advise, then a read after an advise
1958    {ok, Fd8} = ?FILE_MODULE:open(Advise, [write]),
1959    ok = io:format(Fd8, "~s", [Line1]),
1960    ok = io:format(Fd8, "~s", [Line2]),
1961    ok = ?FILE_MODULE:close(Fd8),
1962    {ok, Fd9} = ?FILE_MODULE:open(Advise, [read]),
1963    Offset = 0,
1964    %% same as a 0 length in some implementations
1965    Length = length(Line1) + length(Line2),
1966    ok = ?FILE_MODULE:advise(Fd9, Offset, Length, sequential),
1967    {ok, Line1} = ?FILE_MODULE:read_line(Fd9),
1968    {ok, Line2} = ?FILE_MODULE:read_line(Fd9),
1969    eof = ?FILE_MODULE:read_line(Fd9),
1970    ok = ?FILE_MODULE:close(Fd9),
1971
1972    [] = flush(),
1973    ok.
1974
1975%% Tests that ?FILE_MODULE:allocate/3 at least doesn't crash.
1976allocate(Config) when is_list(Config) ->
1977    PrivDir = proplists:get_value(priv_dir, Config),
1978    Allocate = filename:join(PrivDir,
1979			     atom_to_list(?MODULE)
1980			     ++"_allocate.fil"),
1981
1982    Line1 = "Hello\n",
1983    Line2 = "World!\n",
1984
1985    {ok, Fd} = ?FILE_MODULE:open(Allocate, [write, binary]),
1986    allocate_and_assert(Fd, 1, iolist_size([Line1, Line2])),
1987    ok = io:format(Fd, "~s", [Line1]),
1988    ok = io:format(Fd, "~s", [Line2]),
1989    ok = ?FILE_MODULE:close(Fd),
1990
1991    {ok, Fd2} = ?FILE_MODULE:open(Allocate, [write, binary]),
1992    allocate_and_assert(Fd2, 1, iolist_size(Line1)),
1993    ok = io:format(Fd2, "~s", [Line1]),
1994    ok = io:format(Fd2, "~s", [Line2]),
1995    ok = ?FILE_MODULE:close(Fd2),
1996
1997    {ok, Fd3} = ?FILE_MODULE:open(Allocate, [write, binary]),
1998    allocate_and_assert(Fd3, 1, iolist_size(Line1) + 1),
1999    ok = io:format(Fd3, "~s", [Line1]),
2000    ok = io:format(Fd3, "~s", [Line2]),
2001    ok = ?FILE_MODULE:close(Fd3),
2002
2003    {ok, Fd4} = ?FILE_MODULE:open(Allocate, [write, binary]),
2004    allocate_and_assert(Fd4, 1, 4 * iolist_size([Line1, Line2])),
2005    ok = io:format(Fd4, "~s", [Line1]),
2006    ok = io:format(Fd4, "~s", [Line2]),
2007    ok = ?FILE_MODULE:close(Fd4),
2008
2009    [] = flush(),
2010    ok.
2011
2012allocate_and_assert(Fd, Offset, Length) ->
2013    %% Just verify that calls to ?PRIM_FILE:allocate/3 don't crash or have
2014    %% any other negative side effect. We can't really asssert against a
2015    %% specific return value, because support for file space pre-allocation
2016    %% depends on the OS, OS version and underlying filesystem.
2017    %%
2018    %% The Linux kernel added support for fallocate() in version 2.6.23,
2019    %% which currently works only for the ext4, ocfs2, xfs and btrfs file
2020    %% systems. posix_fallocate() is available in glibc as of version
2021    %% 2.1.94, but it was buggy until glibc version 2.7.
2022    %%
2023    %% Mac OS X, as of version 10.3, supports the fcntl operation F_PREALLOCATE.
2024    %%
2025    %% Solaris supports posix_fallocate() but only for the UFS file system
2026    %% apparently (not supported for ZFS).
2027    %%
2028    %% FreeBSD 9.0 is the first FreeBSD release supporting posix_fallocate().
2029    %%
2030    %% For Windows there's apparently no way to pre-allocate file space, at
2031    %% least with same semantics as posix_fallocate(), fallocate() and
2032    %% fcntl F_PREALLOCATE.
2033    Result = ?FILE_MODULE:allocate(Fd, Offset, Length),
2034    case os:type() of
2035        {win32, _} ->
2036            {error, enotsup} = Result;
2037        _ ->
2038            _ = Result
2039    end.
2040
2041%% Tests that asserts that file:allocate/3 changes file size
2042allocate_file_size(Config) when is_list(Config) ->
2043    case os:type() of
2044        {unix, darwin} ->
2045            PrivDir = proplists:get_value(priv_dir, Config),
2046            Allocate = filename:join(PrivDir, atom_to_list(?MODULE)++"_allocate_file"),
2047
2048            {ok, Fd} = ?FILE_MODULE:open(Allocate, [write]),
2049            ok = ?FILE_MODULE:allocate(Fd, 0, 1024),
2050            {ok, 1024} = ?FILE_MODULE:position(Fd, eof),
2051            ok = ?FILE_MODULE:close(Fd),
2052
2053            [] = flush(),
2054            ok;
2055        {unix, linux} ->
2056            {skip, "file:allocate/3 on Linux does not change file size"};
2057        {win32, _} ->
2058            {skip, "Windows does not support file:allocate/3"};
2059        _ ->
2060            {skip, "Support for allocate/3 is spotty in our test platform at the moment."}
2061    end.
2062
2063%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2064
2065delete(Config) when is_list(Config) ->
2066    RootDir = proplists:get_value(priv_dir,Config),
2067    Name = filename:join(RootDir,
2068			 atom_to_list(?MODULE)
2069			 ++"_delete.fil"),
2070    {ok, Fd1} = ?FILE_MODULE:open(Name, write),
2071    io:format(Fd1,"ok.\n",[]),
2072    ok = ?FILE_MODULE:close(Fd1),
2073    %% Check that the file is readable
2074    {ok, Fd2} = ?FILE_MODULE:open(Name, read),
2075    ok = ?FILE_MODULE:close(Fd2),
2076    ok = ?FILE_MODULE:delete(Name),
2077    %% Check that the file is not readable anymore
2078    {error, _} = ?FILE_MODULE:open(Name, read),
2079    %% Try deleting a nonexistent file
2080    {error, enoent} = ?FILE_MODULE:delete(Name),
2081    [] = flush(),
2082    ok.
2083
2084rename(Config) when is_list(Config) ->
2085    RootDir = proplists:get_value(priv_dir,Config),
2086    FileName1 = atom_to_list(?MODULE)++"_rename.fil",
2087    FileName2 = atom_to_list(?MODULE)++"_rename.ful",
2088    Name1 = filename:join(RootDir, FileName1),
2089    Name2 = filename:join(RootDir, FileName2),
2090    {ok,Fd1} = ?FILE_MODULE:open(Name1,write),
2091    ok = ?FILE_MODULE:close(Fd1),
2092    %% Rename, and check that id really changed name
2093    ok = ?FILE_MODULE:rename(Name1,Name2),
2094    {error, _} = ?FILE_MODULE:open(Name1,read),
2095    {ok,Fd2} = ?FILE_MODULE:open(Name2,read),
2096    ok = ?FILE_MODULE:close(Fd2),
2097    %% Try renaming something to itself
2098    ok = ?FILE_MODULE:rename(Name2,Name2),
2099    %% Try renaming something that doesn't exist
2100    {error, enoent} = ?FILE_MODULE:rename(Name1,Name2),
2101    %% Try renaming to something else than a string
2102    {error, badarg} = ?FILE_MODULE:rename(Name1,{foo,bar}),
2103
2104    %% Move between directories
2105    DirName1 = filename:join(RootDir,
2106			     atom_to_list(?MODULE)
2107			     ++"_rename_dir"),
2108    DirName2 = filename:join(RootDir,
2109			     atom_to_list(?MODULE)
2110			     ++"_second_rename_dir"),
2111    Name1foo = filename:join(DirName1, "foo.fil"),
2112    Name2foo = filename:join(DirName2, "foo.fil"),
2113    Name2bar = filename:join(DirName2, "bar.dir"),
2114    ok = ?FILE_MODULE:make_dir(DirName1),
2115    %% The name has to include the full file name, path in not enough
2116    expect({error, eisdir}, {error, eexist},
2117	   ?FILE_MODULE:rename(Name2,DirName1)),
2118    ok = ?FILE_MODULE:rename(Name2, Name1foo),
2119    %% Now rename the directory
2120    ok = ?FILE_MODULE:rename(DirName1,DirName2),
2121    %% And check that the file is there now
2122    {ok,Fd3} = ?FILE_MODULE:open(Name2foo, read),
2123    ok = ?FILE_MODULE:close(Fd3),
2124    %% Try some dirty things now: move the directory into itself
2125    {error, Msg1} = ?FILE_MODULE:rename(DirName2, Name2bar),
2126    io:format("Errmsg1: ~p",[Msg1]),
2127    %% move dir into a file in itself
2128    {error, Msg2} = ?FILE_MODULE:rename(DirName2, Name2foo),
2129    io:format("Errmsg2: ~p",[Msg2]),
2130
2131    [] = flush(),
2132    ok.
2133
2134%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2135
2136names(Config) when is_list(Config) ->
2137    RootDir = proplists:get_value(priv_dir,Config),
2138    FileName = "foo1.fil",
2139    Name1 = filename:join(RootDir, FileName),
2140    Name2 = [RootDir,"/","foo1",".","fil"],
2141    Name3 = [RootDir,"/",foo,$1,[[[],[],'.']],"f",il],
2142    {ok,Fd0} = ?FILE_MODULE:open(Name1,write),
2143    ok = ?FILE_MODULE:close(Fd0),
2144
2145    %% Try some file names
2146    {ok,Fd1} = ?FILE_MODULE:open(Name1,read),
2147    ok = ?FILE_MODULE:close(Fd1),
2148    {ok,Fd2f} = ?FILE_MODULE:open(lists:flatten(Name2),read),
2149    ok = ?FILE_MODULE:close(Fd2f),
2150    {ok,Fd2} = ?FILE_MODULE:open(Name2,read),
2151    ok = ?FILE_MODULE:close(Fd2),
2152    {ok,Fd3} = ?FILE_MODULE:open(Name3,read),
2153    ok = ?FILE_MODULE:close(Fd3),
2154
2155    %% Now try the same on raw files.
2156    {ok,Fd4} = ?FILE_MODULE:open(Name2, [read, raw]),
2157    ok = ?FILE_MODULE:close(Fd4),
2158    {ok,Fd4f} = ?FILE_MODULE:open(lists:flatten(Name2), [read, raw]),
2159    ok = ?FILE_MODULE:close(Fd4f),
2160    {ok,Fd5} = ?FILE_MODULE:open(Name3, [read, raw]),
2161    ok = ?FILE_MODULE:close(Fd5),
2162
2163    case length(Name1) > 255 of
2164	true ->
2165	    io:format("Path too long for an atom:\n\n~p\n", [Name1]);
2166	false ->
2167	    Name4 = list_to_atom(Name1),
2168	    {ok,Fd6} = ?FILE_MODULE:open(Name4,read),
2169	    ok = ?FILE_MODULE:close(Fd6)
2170    end,
2171
2172    %% Try some path names
2173    Path1 = RootDir,
2174    Path2 = [RootDir],
2175    Path3 = ['',[],[RootDir,[[]]]],
2176    {ok,Fd11,_} = ?FILE_MODULE:path_open([Path1],FileName,read),
2177    ok = ?FILE_MODULE:close(Fd11),
2178    {ok,Fd12,_} = ?FILE_MODULE:path_open([Path2],FileName,read),
2179    ok = ?FILE_MODULE:close(Fd12),
2180    {ok,Fd13,_} = ?FILE_MODULE:path_open([Path3],FileName,read),
2181    ok = ?FILE_MODULE:close(Fd13),
2182    case length(Path1) > 255 of
2183	true->
2184	    io:format("Path too long for an atom:\n\n~p\n", [Path1]);
2185	false ->
2186	    Path4 = list_to_atom(Path1),
2187	    {ok,Fd14,_} = ?FILE_MODULE:path_open([Path4],FileName,read),
2188	    ok = ?FILE_MODULE:close(Fd14)
2189    end,
2190    [] = flush(),
2191    ok.
2192
2193volume_relative_paths(Config) when is_list(Config) ->
2194    case os:type() of
2195        {win32, _} ->
2196            {ok, [Drive, $: | _]} = file:get_cwd(),
2197            %% Relative to current device root.
2198            {ok, RootInfo} = file:read_file_info([Drive, $:, $/]),
2199            {ok, RootInfo} = file:read_file_info("/"),
2200            %% Relative to current device directory.
2201            {ok, DirContents} = file:list_dir([Drive, $:]),
2202            {ok, DirContents} = file:list_dir("."),
2203            [] = flush(),
2204            ok;
2205        _ ->
2206            {skip, "This test is Windows-specific."}
2207    end.
2208
2209unc_paths(Config) when is_list(Config) ->
2210    case os:type() of
2211        {win32, _} ->
2212            %% We assume administrative shares are set up and reachable, and we
2213            %% settle for testing presence as some of the returned data is
2214            %% different.
2215            {ok, _} = file:read_file_info("C:\\Windows\\explorer.exe"),
2216            {ok, _} = file:read_file_info("\\\\localhost\\c$\\Windows\\explorer.exe"),
2217
2218            {ok, Files} = file:list_dir("C:\\Windows\\"),
2219            {ok, Files} = file:list_dir("\\\\localhost\\c$\\Windows\\"),
2220
2221            {ok, Cwd} = file:get_cwd(),
2222
2223            try
2224                ok = file:set_cwd("\\\\localhost\\c$\\Windows\\"),
2225                {ok, _} = file:read_file_info("explorer.exe")
2226            after
2227                file:set_cwd(Cwd)
2228            end,
2229
2230            [] = flush(),
2231            ok;
2232        _ ->
2233            {skip, "This test is Windows-specific."}
2234    end.
2235
2236%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2237
2238
2239e_delete(Config) when is_list(Config) ->
2240    RootDir = proplists:get_value(priv_dir, Config),
2241    Base = filename:join(RootDir,
2242			 atom_to_list(?MODULE)++"_e_delete"),
2243    ok = ?FILE_MODULE:make_dir(Base),
2244
2245    %% Delete a non-existing file.
2246    {error, enoent} =
2247	?FILE_MODULE:delete(filename:join(Base, "non_existing")),
2248
2249    %% Delete a directory.
2250    {error, eperm} = ?FILE_MODULE:delete(Base),
2251
2252    %% Use a path-name with a non-directory component.
2253    Afile = filename:join(Base, "a_file"),
2254    ok = ?FILE_MODULE:write_file(Afile, "hello\n"),
2255    {error, E} =
2256	expect({error, enotdir}, {error, enoent},
2257	       ?FILE_MODULE:delete(filename:join(Afile, "another_file"))),
2258    io:format("Result: ~p~n", [E]),
2259
2260    %% No permission.
2261    case os:type() of
2262	{win32, _} ->
2263	    %% Remove a character device.
2264	    expect({error, eacces}, {error, einval},
2265                   ?FILE_MODULE:delete("nul"));
2266	_ ->
2267	    ?FILE_MODULE:write_file_info(
2268	       Base, #file_info {mode=0}),
2269	    {error, eacces} = ?FILE_MODULE:delete(Afile),
2270	    ?FILE_MODULE:write_file_info(
2271	       Base, #file_info {mode=8#700})
2272    end,
2273
2274    [] = flush(),
2275    ok.
2276
2277%%% FreeBSD gives EEXIST when renaming a file to an empty dir, although the
2278%%% manual page can be interpreted as saying that EISDIR should be given.
2279%%% (What about FreeBSD? We store our nightly build results on a FreeBSD
2280%%% file system, that's what.)
2281
2282e_rename(Config) when is_list(Config) ->
2283    RootDir = proplists:get_value(priv_dir, Config),
2284    Base = filename:join(RootDir,
2285			 atom_to_list(?MODULE)++"_e_rename"),
2286    ok = ?FILE_MODULE:make_dir(Base),
2287
2288    %% Create an empty directory.
2289    EmptyDir = filename:join(Base, "empty_dir"),
2290    ok = ?FILE_MODULE:make_dir(EmptyDir),
2291
2292    %% Create a non-empty directory.
2293    NonEmptyDir = filename:join(Base, "non_empty_dir"),
2294    ok = ?FILE_MODULE:make_dir(NonEmptyDir),
2295    ok = ?FILE_MODULE:write_file(
2296	    filename:join(NonEmptyDir, "a_file"),
2297	    "hello\n"),
2298
2299    %% Create another non-empty directory.
2300    ADirectory = filename:join(Base, "a_directory"),
2301    ok = ?FILE_MODULE:make_dir(ADirectory),
2302    ok = ?FILE_MODULE:write_file(
2303	    filename:join(ADirectory, "a_file"),
2304	    "howdy\n\n"),
2305
2306    %% Create a data file.
2307    File = filename:join(Base, "just_a_file"),
2308    ok = ?FILE_MODULE:write_file(File, "anything goes\n\n"),
2309
2310    %% Move an existing directory to a non-empty directory.
2311    {error, eexist} = ?FILE_MODULE:rename(ADirectory, NonEmptyDir),
2312
2313    %% Move a root directory.
2314    {error, einval} = ?FILE_MODULE:rename("/", "arne"),
2315
2316    %% Move Base into Base/new_name.
2317    {error, einval} =
2318	?FILE_MODULE:rename(Base, filename:join(Base, "new_name")),
2319
2320    %% Overwrite a directory with a file.
2321    expect({error, eexist}, %FreeBSD (?)
2322	   {error, eisdir},
2323	   ?FILE_MODULE:rename(File, EmptyDir)),
2324    expect({error, eexist}, %FreeBSD (?)
2325	   {error, eisdir},
2326	   ?FILE_MODULE:rename(File, NonEmptyDir)),
2327
2328    %% Move a non-existing file.
2329    NonExistingFile = filename:join(Base, "non_existing_file"),
2330    {error, enoent} = ?FILE_MODULE:rename(NonExistingFile, NonEmptyDir),
2331
2332    %% Overwrite a file with a directory.
2333    expect({error, eexist}, %FreeBSD (?)
2334	   {error, enotdir},
2335	   ?FILE_MODULE:rename(ADirectory, File)),
2336
2337    %% Move a file to another filesystem.
2338    %% XXX - This test case is bogus. We cannot be guaranteed that
2339    %%       the source and destination are on
2340    %%       different filesystems.
2341    %%
2342    %% XXX - Gross hack!
2343    Comment = case os:type() of
2344		  {unix, _} ->
2345		      OtherFs = "/tmp",
2346		      NameOnOtherFs = filename:join(OtherFs, filename:basename(File)),
2347		      {ok, Com} = case ?FILE_MODULE:rename(File, NameOnOtherFs) of
2348				      {error, exdev} ->
2349					  %% The file could be in
2350					  %% the same filesystem!
2351					  {ok, ok};
2352				      ok ->
2353					  {ok, {comment,
2354						"Moving between filesystems "
2355						"suceeded, files are probably "
2356						"in the same filesystem!"}};
2357				      {error, eperm} ->
2358					  {ok, {comment, "SBS! You don't "
2359						"have the permission to do "
2360						"this test!"}};
2361				      Else ->
2362					  Else
2363				  end,
2364		      Com;
2365		  {win32, _} ->
2366		      %% At least Windows NT can
2367		      %% successfully move a file to
2368		      %% another drive.
2369		      ok
2370	      end,
2371    [] = flush(),
2372    Comment.
2373
2374%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2375
2376e_make_dir(Config) when is_list(Config) ->
2377    RootDir = proplists:get_value(priv_dir, Config),
2378    Base = filename:join(RootDir,
2379			 atom_to_list(?MODULE)++"_e_make_dir"),
2380    ok = ?FILE_MODULE:make_dir(Base),
2381
2382    %% A component of the path does not exist.
2383    {error, enoent} = ?FILE_MODULE:make_dir(filename:join([Base, "a", "b"])),
2384
2385    %% Use a path-name with a non-directory component.
2386    Afile = filename:join(Base, "a_directory"),
2387    ok = ?FILE_MODULE:write_file(Afile, "hello\n"),
2388    case ?FILE_MODULE:make_dir(
2389            filename:join(Afile, "another_directory")) of
2390        {error, enotdir} -> io:format("Result: enotdir");
2391        {error, enoent} -> io:format("Result: enoent")
2392    end,
2393
2394    %% No permission (on Unix only).
2395    case os:type() of
2396	{win32, _} ->
2397	    ok;
2398	_ ->
2399	    ?FILE_MODULE:write_file_info(Base, #file_info {mode=0}),
2400	    {error, eacces} = ?FILE_MODULE:make_dir(filename:join(Base, "xxxx")),
2401	    ?FILE_MODULE:write_file_info(
2402	       Base, #file_info {mode=8#700})
2403    end,
2404    ok.
2405
2406%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2407
2408e_del_dir(Config) when is_list(Config) ->
2409    RootDir = proplists:get_value(priv_dir, Config),
2410    Base = test_server:temp_name(filename:join(RootDir, "e_del_dir")),
2411    io:format("Base: ~p", [Base]),
2412    ok = ?FILE_MODULE:make_dir(Base),
2413
2414    %% Delete a non-existent directory.
2415    {error, enoent} =
2416	?FILE_MODULE:del_dir(filename:join(Base, "non_existing")),
2417
2418    %% Use a path-name with a non-directory component.
2419    Afile = filename:join(Base, "a_directory"),
2420    ok = ?FILE_MODULE:write_file(Afile, "hello\n"),
2421    {error, E1} = expect({error, enotdir}, {error, enoent},
2422			 ?FILE_MODULE:del_dir(
2423			    filename:join(Afile, "another_directory"))),
2424    io:format("Result: ~p", [E1]),
2425
2426    %% Delete a non-empty directory.
2427    {error, E2} = expect({error, enotempty}, {error, eexist}, {error, eacces},
2428			 ?FILE_MODULE:del_dir(Base)),
2429    io:format("Result: ~p", [E2]),
2430
2431    %% Remove the current directory.
2432    {error, E3} = expect({error, einval},
2433			 {error, eperm}, % Linux and DUX
2434			 {error, eacces},
2435			 {error, ebusy},
2436			 ?FILE_MODULE:del_dir(".")),
2437    io:format("Result: ~p", [E3]),
2438
2439    %% No permission.
2440    case os:type() of
2441	{win32, _} ->
2442	    ok;
2443	_ ->
2444	    ADirectory = filename:join(Base, "no_perm"),
2445	    ok = ?FILE_MODULE:make_dir(ADirectory),
2446	    ?FILE_MODULE:write_file_info( Base, #file_info {mode=0}),
2447	    {error, eacces} = ?FILE_MODULE:del_dir(ADirectory),
2448	    ?FILE_MODULE:write_file_info( Base, #file_info {mode=8#700})
2449    end,
2450    [] = flush(),
2451    ok.
2452
2453%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2454
2455
2456%% Trying reading and positioning from a compressed file.
2457
2458read_compressed_cooked(Config) when is_list(Config) ->
2459    Data = proplists:get_value(data_dir, Config),
2460    Real = filename:join(Data, "realmen.html.gz"),
2461    {ok, Fd} = ?FILE_MODULE:open(Real, [read,compressed]),
2462    try_read_file_list(Fd).
2463
2464read_compressed_cooked_binary(Config) when is_list(Config) ->
2465    Data = proplists:get_value(data_dir, Config),
2466    Real = filename:join(Data, "realmen.html.gz"),
2467    {ok, Fd} = ?FILE_MODULE:open(Real, [read,compressed,binary]),
2468    try_read_file_binary(Fd).
2469
2470%% Trying reading and positioning from an uncompressed file,
2471%% but with the compressed flag given.
2472
2473read_not_really_compressed(Config) when is_list(Config) ->
2474    Data = proplists:get_value(data_dir, Config),
2475    Priv = proplists:get_value(priv_dir, Config),
2476
2477    %% The file realmen.html might have got CRs added (by WinZip).
2478    %% Remove them, or the file positions will not be correct.
2479
2480    Real = filename:join(Data, "realmen.html"),
2481    RealPriv = filename:join(Priv,
2482			     atom_to_list(?MODULE)++"_realmen.html"),
2483    {ok, RealDataBin} = ?FILE_MODULE:read_file(Real),
2484    RealData = remove_crs(binary_to_list(RealDataBin), []),
2485    ok = ?FILE_MODULE:write_file(RealPriv, RealData),
2486    {ok, Fd} = ?FILE_MODULE:open(RealPriv, [read, compressed]),
2487    try_read_file_list(Fd).
2488
2489remove_crs([$\r|Rest], Result) ->
2490    remove_crs(Rest, Result);
2491remove_crs([C|Rest], Result) ->
2492    remove_crs(Rest, [C|Result]);
2493remove_crs([], Result) ->
2494    lists:reverse(Result).
2495
2496try_read_file_list(Fd) ->
2497    %% Seek to the current position (nothing should happen).
2498
2499    {ok, 0} = ?FILE_MODULE:position(Fd, 0),
2500    {ok, 0} = ?FILE_MODULE:position(Fd, {cur, 0}),
2501
2502    %% Read a few lines from a compressed file.
2503
2504    ShouldBe = "<TITLE>Real Programmers Don't Use PASCAL</TITLE>\n",
2505    ShouldBe = io:get_line(Fd, ''),
2506
2507    %% Now seek forward.
2508
2509    {ok, 381} = ?FILE_MODULE:position(Fd, 381),
2510    Back = "Back in the good old days -- the \"Golden Era\" " ++
2511	"of computers, it was\n",
2512    Back = io:get_line(Fd, ''),
2513
2514    %% Try to search forward relative to the current position.
2515
2516    {ok, CurPos} = ?FILE_MODULE:position(Fd, {cur, 0}),
2517    RealPos = 4273,
2518    {ok, RealPos} = ?FILE_MODULE:position(Fd, {cur, RealPos-CurPos}),
2519    RealProg = "<LI> Real Programmers aren't afraid to use GOTOs.\n",
2520    RealProg = io:get_line(Fd, ''),
2521
2522    %% Seek backward.
2523
2524    AfterTitle = length("<TITLE>"),
2525    {ok, AfterTitle} = ?FILE_MODULE:position(Fd, AfterTitle),
2526    Title = "Real Programmers Don't Use PASCAL</TITLE>\n",
2527    Title = io:get_line(Fd, ''),
2528
2529    %% Seek past the end of the file.
2530
2531    {ok, _} = ?FILE_MODULE:position(Fd, 25000),
2532
2533    %% Done.
2534
2535    ?FILE_MODULE:close(Fd),
2536    [] = flush(),
2537    ok.
2538
2539try_read_file_binary(Fd) ->
2540    %% Seek to the current position (nothing should happen).
2541
2542    {ok, 0} = ?FILE_MODULE:position(Fd, 0),
2543    {ok, 0} = ?FILE_MODULE:position(Fd, {cur, 0}),
2544
2545    %% Read a few lines from a compressed file.
2546
2547    ShouldBe = <<"<TITLE>Real Programmers Don't Use PASCAL</TITLE>\n">>,
2548    ShouldBe = io:get_line(Fd, ''),
2549
2550    %% Now seek forward.
2551
2552    {ok, 381} = ?FILE_MODULE:position(Fd, 381),
2553    Back = <<"Back in the good old days -- the \"Golden Era\" "
2554	     "of computers, it was\n">>,
2555    Back = io:get_line(Fd, ''),
2556
2557    %% Try to search forward relative to the current position.
2558
2559    {ok, CurPos} = ?FILE_MODULE:position(Fd, {cur, 0}),
2560    RealPos = 4273,
2561    {ok, RealPos} = ?FILE_MODULE:position(Fd, {cur, RealPos-CurPos}),
2562    RealProg = <<"<LI> Real Programmers aren't afraid to use GOTOs.\n">>,
2563    RealProg = io:get_line(Fd, ''),
2564
2565    %% Seek backward.
2566
2567    AfterTitle = length("<TITLE>"),
2568    {ok, AfterTitle} = ?FILE_MODULE:position(Fd, AfterTitle),
2569    Title = <<"Real Programmers Don't Use PASCAL</TITLE>\n">>,
2570    Title = io:get_line(Fd, ''),
2571
2572    %% Done.
2573
2574    ?FILE_MODULE:close(Fd),
2575    [] = flush(),
2576    ok.
2577
2578read_cooked_tar_problem(Config) when is_list(Config) ->
2579    Data = proplists:get_value(data_dir, Config),
2580    ProblemFile = filename:join(Data, "cooked_tar_problem.tar.gz"),
2581    {ok,Fd} = ?FILE_MODULE:open(ProblemFile, [read,compressed,binary]),
2582
2583    {ok,34304} = file:position(Fd, 34304),
2584    {ok,Bin} = file:read(Fd, 512),
2585    512 = byte_size(Bin),
2586
2587    {ok,34304+512+1024} = file:position(Fd, {cur,1024}),
2588
2589    ok = file:close(Fd),
2590
2591    ok.
2592
2593
2594%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2595
2596write_compressed(Config) when is_list(Config) ->
2597    Priv = proplists:get_value(priv_dir, Config),
2598    MyFile = filename:join(Priv,
2599			   atom_to_list(?MODULE)++"_test.gz"),
2600
2601    %% Write a file.
2602
2603    {ok, Fd} = ?FILE_MODULE:open(MyFile, [write, compressed]),
2604    {ok, 0} = ?FILE_MODULE:position(Fd, 0),
2605    Prefix = "hello\n",
2606    End = "end\n",
2607    ok = io:put_chars(Fd, Prefix),
2608    {ok, 143} = ?FILE_MODULE:position(Fd, 143),
2609    ok = io:put_chars(Fd, End),
2610    ok = ?FILE_MODULE:close(Fd),
2611
2612    %% Read the file and verify the contents.
2613
2614    {ok, Fd1} = ?FILE_MODULE:open(MyFile, [read, compressed]),
2615    Prefix = io:get_line(Fd1, ''),
2616    Second = lists:duplicate(143-length(Prefix), 0) ++ End,
2617    Second = io:get_line(Fd1, ''),
2618    ok = ?FILE_MODULE:close(Fd1),
2619
2620    %% Verify successful compression by uncompressing the file
2621    %% using zlib:gunzip/1.
2622
2623    {ok,Contents} = file:read_file(MyFile),
2624    <<"hello\n",0:137/unit:8,"end\n">> = zlib:gunzip(Contents),
2625
2626    %% Ensure that the file is compressed.
2627
2628    TotalSize = 143 + length(End),
2629    case ?FILE_MODULE:read_file_info(MyFile) of
2630	{ok, #file_info{size=Size}} when Size < TotalSize ->
2631	    ok;
2632	{ok, #file_info{size=Size}} when Size == TotalSize ->
2633	    ct:fail(file_not_compressed)
2634    end,
2635
2636    %% Write again to ensure that the file is truncated.
2637
2638    {ok, Fd2} = ?FILE_MODULE:open(MyFile, [write, compressed]),
2639    NewString = "aaaaaaaaaaa",
2640    ok = io:put_chars(Fd2, NewString),
2641    ok = ?FILE_MODULE:close(Fd2),
2642    {ok, Fd3} = ?FILE_MODULE:open(MyFile, [read, compressed]),
2643    {ok, NewString} = ?FILE_MODULE:read(Fd3, 1024),
2644    ok = ?FILE_MODULE:close(Fd3),
2645
2646    %% Done.
2647
2648    [] = flush(),
2649    ok.
2650
2651%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2652
2653catenated_gzips(Config) when is_list(Config) ->
2654    Priv = proplists:get_value(priv_dir, Config),
2655    MyFile = filename:join(Priv, ?MODULE_STRING++"_test.gz"),
2656
2657    First = "Hello, all good men going to search parties. ",
2658    Second = "Now I really need your help.",
2659    All = iolist_to_binary([First|Second]),
2660    Cat = [zlib:gzip(First),zlib:gzip(Second)],
2661
2662    ok = file:write_file(MyFile, Cat),
2663
2664    {ok,Fd} = file:open(MyFile, [read,compressed,binary]),
2665    {ok,All} = file:read(Fd, 100000),
2666    ok = file:close(Fd),
2667
2668    ok.
2669
2670
2671%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2672
2673compress_errors(Config) when is_list(Config) ->
2674    DataDir =
2675	filename:dirname(
2676	  filename:join(proplists:get_value(data_dir, Config), "x")),
2677    DataDirSlash = DataDir++"/",
2678    {error, enoent} = ?FILE_MODULE:open("non_existing__",
2679					[compressed, read]),
2680    {error, einval} = ?FILE_MODULE:open("non_existing__",
2681					[compressed, read, write]),
2682    {error, einval} = ?FILE_MODULE:open("non_existing__",
2683					[compressed, read, append]),
2684    {error, einval} = ?FILE_MODULE:open("non_existing__",
2685					[compressed, write, append]),
2686    {error, E1} = ?FILE_MODULE:open(DataDir, [compressed, read]),
2687    {error, E2} = ?FILE_MODULE:open(DataDirSlash, [compressed, read]),
2688    {error, E3} = ?FILE_MODULE:open(DataDir, [compressed, write]),
2689    {error, E4} = ?FILE_MODULE:open(DataDirSlash, [compressed, write]),
2690    {eisdir,eisdir,eisdir,eisdir} = {E1,E2,E3,E4},
2691
2692    %% Read a corrupted .gz file.
2693
2694    Corrupted = filename:join(DataDir, "corrupted.gz"),
2695    {ok, Fd} = ?FILE_MODULE:open(Corrupted, [read, compressed]),
2696    {error, eio} = ?FILE_MODULE:read(Fd, 100),
2697    ?FILE_MODULE:close(Fd),
2698
2699    [] = flush(),
2700    ok.
2701
2702%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2703
2704compress_async_crash(Config) when is_list(Config) ->
2705    DataDir = proplists:get_value(data_dir, Config),
2706    Path = filename:join(DataDir, "test.gz"),
2707    ExpectedData = <<"qwerty">>,
2708
2709    _ = ?FILE_MODULE:delete(Path),
2710    {ok, Fd} = ?FILE_MODULE:open(Path, [write, binary, compressed]),
2711    ok = ?FILE_MODULE:write(Fd, ExpectedData),
2712    ok = ?FILE_MODULE:close(Fd),
2713
2714    %% Test that when using async thread pool, the emulator doesn't crash
2715    %% when the efile port driver is stopped while a compressed file operation
2716    %% is in progress (being carried by an async thread).
2717    ok = compress_async_crash_loop(10000, Path, ExpectedData),
2718    ok = ?FILE_MODULE:delete(Path),
2719    ok.
2720
2721compress_async_crash_loop(0, _Path, _ExpectedData) ->
2722    ok;
2723compress_async_crash_loop(N, Path, ExpectedData) ->
2724    Parent = self(),
2725    {Pid, Ref} = spawn_monitor(
2726		   fun() ->
2727			   {ok, Fd} = ?FILE_MODULE:open(
2728					 Path, [read, compressed, raw, binary]),
2729			   Len = byte_size(ExpectedData),
2730			   Parent ! {self(), continue},
2731			   {ok, ExpectedData} = ?FILE_MODULE:read(Fd, Len),
2732			   ok = ?FILE_MODULE:close(Fd),
2733			   receive foobar -> ok end
2734		   end),
2735    receive
2736        {Pid, continue} ->
2737            exit(Pid, shutdown),
2738            receive
2739                {'DOWN', Ref, _, _, Reason} ->
2740                    shutdown = Reason
2741            end;
2742        {'DOWN', Ref, _, _, Reason2} ->
2743            ct:fail({worker_exited, Reason2})
2744    after 60000 ->
2745            exit(Pid, shutdown),
2746            erlang:demonitor(Ref, [flush]),
2747            ct:fail(worker_timeout)
2748    end,
2749    compress_async_crash_loop(N - 1, Path, ExpectedData).
2750
2751%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2752
2753unicode(Config) when is_list(Config) ->
2754    Dir = proplists:get_value(priv_dir, Config),
2755    Name = filename:join(Dir, "data-utf8.txt"),
2756    Txt = lists:seq(128, 255),
2757    D = unicode:characters_to_binary(Txt, latin1, latin1),
2758    {ok,Fd1} =
2759	?FILE_MODULE:open(Name, [write,read,binary,{encoding,unicode}]),
2760    ok = ?FILE_MODULE:truncate(Fd1),
2761    ok = ?FILE_MODULE:write(Fd1, Txt),
2762    {ok,0} = ?FILE_MODULE:position(Fd1, bof),
2763    {ok,D} = ?FILE_MODULE:read(Fd1, 129),
2764    {ok,0} = ?FILE_MODULE:position(Fd1, bof),
2765    {ok,D1} = ?FILE_MODULE:read(Fd1, 64),
2766    {ok,Pos} = ?FILE_MODULE:position(Fd1, cur),
2767    {ok,D2} = ?FILE_MODULE:pread(Fd1, {cur,0}, 65),
2768    D = <<D1/binary, D2/binary>>,
2769    {ok,D1} = ?FILE_MODULE:pread(Fd1, bof, 64),
2770    {ok,Pos} = ?FILE_MODULE:position(Fd1, Pos),
2771    {ok,D2} = ?FILE_MODULE:read(Fd1, 64),
2772    ok = ?FILE_MODULE:close(Fd1),
2773    %%
2774    RawD = unicode:characters_to_binary(Txt, latin1, unicode),
2775    {ok,RawD} = ?FILE_MODULE:read_file(Name),
2776    %%
2777    {ok,Fd2} = ?FILE_MODULE:open(Name, [read,{encoding,unicode}]),
2778    {ok,Txt} = ?FILE_MODULE:read(Fd2, 129),
2779    {Txt1,Txt2} = lists:split(64, Txt),
2780    {ok,Txt2} = ?FILE_MODULE:pread(Fd2, Pos, 65),
2781    {ok,0} = ?FILE_MODULE:position(Fd2, bof),
2782    {ok,Txt1} = ?FILE_MODULE:read(Fd2, 64),
2783    ok = ?FILE_MODULE:close(Fd2).
2784
2785%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2786
2787%% Test the file:altname/1 function.
2788altname(Config) when is_list(Config) ->
2789    RootDir = proplists:get_value(priv_dir, Config),
2790    NewDir = filename:join(RootDir,
2791			   "long alternative path name with spaces"),
2792    ok = ?FILE_MODULE:make_dir(NewDir),
2793    Name = filename:join(NewDir, "a_file_with_long_name"),
2794    ShortName = filename:join(NewDir, "short"),
2795    NonexName = filename:join(NewDir, "nonexistent"),
2796    ok = ?FILE_MODULE:write_file(Name, "some contents\n"),
2797    ok = ?FILE_MODULE:write_file(ShortName, "some contents\n"),
2798    Result =
2799	case ?FILE_MODULE:altname(NewDir) of
2800	    {error, enotsup} ->
2801		{skipped, "Altname not supported on this platform"};
2802	    {ok, "LONGAL~1"} ->
2803		{ok, "A_FILE~1"} = ?FILE_MODULE:altname(Name),
2804		{ok, "c:/"} = ?FILE_MODULE:altname("C:/"),
2805		{ok, "c:/"} = ?FILE_MODULE:altname("C:\\"),
2806		{error,enoent} = ?FILE_MODULE:altname(NonexName),
2807		{ok, "short"} = ?FILE_MODULE:altname(ShortName),
2808		ok
2809	end,
2810    [] = flush(),
2811    Result.
2812
2813
2814%% Test creating a hard link.
2815make_link(Config) when is_list(Config) ->
2816    RootDir = proplists:get_value(priv_dir, Config),
2817    NewDir = filename:join(RootDir,
2818			   atom_to_list(?MODULE)
2819			   ++"_make_link"),
2820    ok = ?FILE_MODULE:make_dir(NewDir),
2821
2822    Name = filename:join(NewDir, "a_file"),
2823    ok = ?FILE_MODULE:write_file(Name, "some contents\n"),
2824
2825    Alias = filename:join(NewDir, "an_alias"),
2826    Result =
2827	case ?FILE_MODULE:make_link(Name, Alias) of
2828	    {error, enotsup} ->
2829		{skipped, "Links not supported on this platform"};
2830	    ok ->
2831		%% Note: We take the opportunity to test
2832		%% ?FILE_MODULE:read_link_info/1,
2833		%% which should in behave exactly as
2834		%% ?FILE_MODULE:read_file_info/1
2835		%% since they are not used on symbolic links.
2836
2837		{ok, Info} = ?FILE_MODULE:read_link_info(Name),
2838                {ok,Info} = ?FILE_MODULE:read_link_info(Name, [raw]),
2839		{ok, Info} = ?FILE_MODULE:read_link_info(Alias),
2840                {ok,Info} = ?FILE_MODULE:read_link_info(Alias, [raw]),
2841		#file_info{links = 2, type = regular} = Info,
2842		{error, eexist} =
2843		    ?FILE_MODULE:make_link(Name, Alias),
2844		ok
2845	end,
2846
2847    [] = flush(),
2848    Result.
2849
2850%% Test that reading link info for an ordinary file or directory works
2851%% (on all platforms).
2852read_link_info_for_non_link(Config) when is_list(Config) ->
2853    {ok, #file_info{type=directory}} =
2854	?FILE_MODULE:read_link_info("."),
2855    {ok, #file_info{type=directory}} = ?FILE_MODULE:read_link_info(".", [raw]),
2856
2857    [] = flush(),
2858    ok.
2859
2860%% Test operations on symbolic links (for Unix).
2861symlinks(Config) when is_list(Config) ->
2862    {error, _} = ?FILE_MODULE:read_link(lists:duplicate(10000,$a)),
2863    {error, _} = ?FILE_MODULE:read_link_all(lists:duplicate(10000,$a)),
2864    RootDir = proplists:get_value(priv_dir, Config),
2865    NewDir = filename:join(RootDir,
2866			   atom_to_list(?MODULE)
2867			   ++"_symlinks"),
2868    ok = ?FILE_MODULE:make_dir(NewDir),
2869
2870    Name = filename:join(NewDir, "a_plain_file"),
2871    ok = ?FILE_MODULE:write_file(Name, "some stupid content\n"),
2872
2873    Alias = filename:join(NewDir, "a_symlink_alias"),
2874    Result =
2875	case ?FILE_MODULE:make_symlink(Name, Alias) of
2876	    {error, enotsup} ->
2877		{skipped, "Links not supported on this platform"};
2878	    {error, eperm} ->
2879		{win32,_} = os:type(),
2880		{skipped, "Windows user not privileged to create symlinks"};
2881	    ok ->
2882		{ok, Info1} = ?FILE_MODULE:read_file_info(Name),
2883                {ok,Info1} = ?FILE_MODULE:read_file_info(Name, [raw]),
2884		{ok, Info1} = ?FILE_MODULE:read_file_info(Alias),
2885                {ok,Info1} = ?FILE_MODULE:read_file_info(Alias, [raw]),
2886		{ok, Info1} = ?FILE_MODULE:read_link_info(Name),
2887                {ok,Info1} = ?FILE_MODULE:read_link_info(Name, [raw]),
2888		#file_info{links = 1, type = regular} = Info1,
2889
2890		{ok, Info2} = ?FILE_MODULE:read_link_info(Alias),
2891                {ok,Info2} = ?FILE_MODULE:read_link_info(Alias, [raw]),
2892		#file_info{links=1, type=symlink} = Info2,
2893		{ok, Name} = ?FILE_MODULE:read_link(Alias),
2894		{ok, Name} = ?FILE_MODULE:read_link_all(Alias),
2895		%% If all is good, delete dir again (avoid hanging dir on windows)
2896		rm_rf(?FILE_MODULE,NewDir),
2897		ok
2898	end,
2899
2900    [] = flush(),
2901    Result.
2902
2903%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2904
2905copy(Config) when is_list(Config) ->
2906    RootDir = proplists:get_value(priv_dir, Config),
2907    %% Create a text file.
2908    Name1 = filename:join(RootDir, atom_to_list(?MODULE)++"_copy_1.txt"),
2909    Line = "The quick brown fox jumps over a lazy dog. 0123456789\n",
2910    Len = length(Line),
2911    {ok, Handle1} = ?FILE_MODULE:open(Name1, [write]),
2912    {_, Size1} =
2913	iterate({0, 0},
2914		done,
2915		fun({_, S}) when S >= 128*1024 ->
2916			done;
2917		   ({N, S}) ->
2918			H = integer_to_list(N),
2919			ok = ?FILE_MODULE:write(Handle1, [H, " ", Line]),
2920			{N + 1, S + length(H) + 1 + Len}
2921		end),
2922    ?FILE_MODULE:close(Handle1),
2923    %% Make a copy
2924    Name2 = filename:join(RootDir, atom_to_list(?MODULE)++"_copy_2.txt"),
2925    {ok, Size1} = ?FILE_MODULE:copy(Name1, Name2),
2926    %% Concatenate 1
2927    Name3 = filename:join(RootDir, atom_to_list(?MODULE)++"_copy_3.txt"),
2928    {ok, Handle3} = ?FILE_MODULE:open(Name3, [raw, write, binary]),
2929    {ok, Size1} = ?FILE_MODULE:copy(Name1, Handle3),
2930    {ok, Handle2} = ?FILE_MODULE:open(Name2, [read, binary]),
2931    {ok, Size1} = ?FILE_MODULE:copy(Handle2, Handle3),
2932    ok = ?FILE_MODULE:close(Handle3),
2933    ok = ?FILE_MODULE:close(Handle2),
2934    %% Concatenate 2
2935    Name4 = filename:join(RootDir, atom_to_list(?MODULE)++"_copy_4.txt"),
2936    {ok, Handle4} = ?FILE_MODULE:open(Name4, [write, binary]),
2937    {ok, Size1} = ?FILE_MODULE:copy(Name1, Handle4),
2938    {ok, Handle5} = ?FILE_MODULE:open(Name2, [raw, read, binary]),
2939    {ok, Size1} = ?FILE_MODULE:copy(Handle5, Handle4),
2940    ok = ?FILE_MODULE:close(Handle5),
2941    ok = ?FILE_MODULE:close(Handle4),
2942    %% %% Just for test of the test
2943    %% {ok, Handle2q} = ?FILE_MODULE:open(Name2, [write, append]),
2944    %% ok = ?FILE_MODULE:write(Handle2q, "q"),
2945    %% ok = ?FILE_MODULE:close(Handle2q),
2946    %% Compare the files
2947    {ok, Handle1a} = ?FILE_MODULE:open(Name1, [raw, read]),
2948    {ok, Handle2a} = ?FILE_MODULE:open(Name2, [raw, read]),
2949    true = stream_cmp(fd_stream_factory([Handle1a]),
2950		      fd_stream_factory([Handle2a])),
2951    {ok, 0} = ?FILE_MODULE:position(Handle1a, 0),
2952    {ok, 0} = ?FILE_MODULE:position(Handle2a, 0),
2953    {ok, Handle3a} = ?FILE_MODULE:open(Name3, [raw, read]),
2954    true = stream_cmp(fd_stream_factory([Handle1a, Handle2a]),
2955		      fd_stream_factory([Handle2a])),
2956    ok = ?FILE_MODULE:close(Handle1a),
2957    ok = ?FILE_MODULE:close(Handle2a),
2958    ok = ?FILE_MODULE:close(Handle3a),
2959    [] = flush(),
2960    ok.
2961
2962
2963
2964fd_stream_factory([]) ->
2965    [];
2966fd_stream_factory([Fd | T] = L) ->
2967    fun() ->
2968	    case ?FILE_MODULE:read(Fd, 8192) of
2969		{ok, Data} when is_binary(Data) ->
2970		    binary_to_list(Data) ++ fd_stream_factory(L);
2971		{ok, Data} when is_list(Data) ->
2972		    Data ++ fd_stream_factory(L);
2973		eof ->
2974		    fd_stream_factory(T);
2975		{error, _} = Error ->
2976		    Error
2977	    end
2978    end.
2979
2980
2981
2982stream_cmp(F1, F2) when is_function(F1), is_function(F2) ->
2983    stream_cmp(F1(), F2());
2984stream_cmp(F, X) when is_function(F) ->
2985    stream_cmp(F(), X);
2986stream_cmp(X, F) when is_function(F) ->
2987    stream_cmp(X, F());
2988stream_cmp({error, _} = Error, _) ->
2989    Error;
2990stream_cmp(_, {error, _} = Error) ->
2991    Error;
2992stream_cmp([], []) ->
2993    true;
2994stream_cmp([], [_|_]) ->
2995    false;
2996stream_cmp([_|_], []) ->
2997    false;
2998stream_cmp([H | T1], [H | T2]) ->
2999    stream_cmp(T1, T2).
3000
3001%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3002
3003%% Test the get_cwd(), open(), and copy() file server calls.
3004new_slave(_RootDir, Cwd) ->
3005    L = "qwertyuiopasdfghjklzxcvbnm",
3006    N = length(L),
3007    {ok, Cwd}         = ?FILE_MODULE:get_cwd(),
3008    {error, enotsup}  = ?FILE_MODULE:get_cwd("C:"), % Unix only testcase
3009    {ok, FD1}         = ?FILE_MODULE:open("file1.txt", write),
3010    ok                = ?FILE_MODULE:close(FD1),
3011    {ok, FD2}         = ?FILE_MODULE:open("file1.txt",
3012					  [write, append,
3013					   binary, compressed,
3014					   delayed_write,
3015					   {delayed_write, 0, 0},
3016					   read_ahead,
3017					   {read_ahead, 0}]),
3018    ok                = ?FILE_MODULE:write(FD2, L),
3019    ok                = ?FILE_MODULE:close(FD2),
3020    {ok, N2}          = ?FILE_MODULE:copy("file1.txt", "file2.txt"),
3021    io:format("Size ~p, compressed ~p.~n", [N, N2]),
3022    {ok, FD3}         = ?FILE_MODULE:open("file2.txt",
3023					  [binary, compressed]),
3024    %% The file_io_server will translate the binary into a list
3025    {ok, L}           = ?FILE_MODULE:read(FD3, N+1),
3026    ok                = ?FILE_MODULE:close(FD3),
3027    %%
3028    ok                = ?FILE_MODULE:delete("file1.txt"),
3029    ok                = ?FILE_MODULE:delete("file2.txt"),
3030    []                = flush(),
3031    ok.
3032
3033
3034%% Test the get_cwd() and open() file server calls.
3035old_slave(_RootDir, Cwd) ->
3036    L = "qwertyuiopasdfghjklzxcvbnm",
3037    N = length(L),
3038    {ok, Cwd}         = ?FILE_MODULE:get_cwd(),
3039    {error, enotsup}  = ?FILE_MODULE:get_cwd("C:"), % Unix only testcase
3040    {ok, FD1}         = ?FILE_MODULE:open("file1.txt", write),
3041    ok                = ?FILE_MODULE:close(FD1),
3042    {ok, FD2}         = ?FILE_MODULE:open("file1.txt",
3043					  [write, binary, compressed]),
3044    ok                = ?FILE_MODULE:write(FD2, L),
3045    ok                = ?FILE_MODULE:close(FD2),
3046    {ok, FD3}         = ?FILE_MODULE:open("file1.txt", [write, append]),
3047    ok                = ?FILE_MODULE:close(FD3),
3048    {ok, FD4}         = ?FILE_MODULE:open("file1.txt",
3049					  [binary, compressed]),
3050    %% The file_io_server will translate the binary into a list
3051    {ok, L}           = ?FILE_MODULE:read(FD4, N+1),
3052    ok                = ?FILE_MODULE:close(FD4),
3053    %%
3054    ok                = ?FILE_MODULE:delete("file1.txt"),
3055    []                = flush(),
3056    ok.
3057
3058run_test(Test, Args) ->
3059    case (catch apply(?MODULE, Test, Args)) of
3060	{'EXIT', _} = Exit ->
3061	    {done, Exit, get(test_server_loc)};
3062	Result ->
3063	    {done, Result}
3064    end.
3065
3066%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3067
3068%% Tests the file open option {delayed_write, Size, Delay}.
3069
3070delayed_write(Config) when is_list(Config) ->
3071    RootDir = proplists:get_value(priv_dir, Config),
3072    File = filename:join(RootDir,
3073			 atom_to_list(?MODULE)++"_delayed_write.txt"),
3074    Data1 = "asdfghjkl",
3075    Data2 = "qwertyuio",
3076    Data3 = "zxcvbnm,.",
3077    Size = length(Data1),
3078    Size = length(Data2),
3079    Size = length(Data3),
3080    Data1Data1 = Data1++Data1,
3081    Data1Data1Data1 = Data1Data1++Data1,
3082    Data1Data1Data1Data1 = Data1Data1++Data1Data1,
3083    %%
3084    %% Test caching and normal close of non-raw file
3085    {ok, Fd1} =
3086	?FILE_MODULE:open(File, [write, {delayed_write, Size+1, 400}]),
3087    ok = ?FILE_MODULE:write(Fd1, Data1),
3088    %% Wait for a reasonable amount of time to check whether the write was
3089    %% practically instantaneous or actually delayed.
3090    timer:sleep(100),
3091    {ok, Fd2} = ?FILE_MODULE:open(File, [read]),
3092    eof = ?FILE_MODULE:read(Fd2, 1),
3093    ok = ?FILE_MODULE:write(Fd1, Data1), % Data flush on size
3094    timer:sleep(100),
3095    {ok, Data1Data1} = ?FILE_MODULE:pread(Fd2, bof, 2*Size+1),
3096    ok = ?FILE_MODULE:write(Fd1, Data1),
3097    timer:sleep(500), % Wait until data flush on timeout
3098    {ok, Data1Data1Data1} = ?FILE_MODULE:pread(Fd2, bof, 3*Size+1),
3099    ok = ?FILE_MODULE:write(Fd1, Data1),
3100    ok = ?FILE_MODULE:close(Fd1), % Data flush on close
3101    timer:sleep(100),
3102    {ok, Data1Data1Data1Data1} = ?FILE_MODULE:pread(Fd2, bof, 4*Size+1),
3103    ok = ?FILE_MODULE:close(Fd2),
3104    %%
3105    %% Test implicit close through exit by file owning process,
3106    %% raw file, default parameters.
3107    Parent = self(),
3108    Fun = fun() ->
3109		  Child = self(),
3110		  Test =
3111		      fun () ->
3112			      {ok, Fd} = ?FILE_MODULE:open(File,
3113							   [raw, write, delayed_write]),
3114			      ok = ?FILE_MODULE:write(Fd, Data1),
3115			      Parent ! {Child, wrote},
3116			      receive
3117				  {Parent, continue, Reason} ->
3118				      {ok, Reason}
3119			      end
3120		      end,
3121		  case (catch Test()) of
3122		      {ok, Reason} -> exit(Reason);
3123		      Unknown ->
3124			  exit({Unknown, get(test_server_loc)})
3125		  end
3126	  end,
3127    Child1 = spawn(Fun),
3128    Mref1 = erlang:monitor(process, Child1),
3129    receive
3130        {Child1, wrote} ->
3131            ok;
3132        {'DOWN', Mref1, _, _, _} = Down1a ->
3133            ct:fail(Down1a)
3134    end,
3135    timer:sleep(100), % Just in case the file system is slow
3136    {ok, Fd3} = ?FILE_MODULE:open(File, [read]),
3137    eof = ?FILE_MODULE:read(Fd3, 1),
3138    Child1 ! {Parent, continue, normal},
3139    receive
3140        {'DOWN', Mref1, process, Child1, normal} ->
3141            ok;
3142        {'DOWN', Mref1, _, _, _} = Down1b ->
3143            ct:fail(Down1b)
3144    end,
3145    timer:sleep(100), % Just in case the file system is slow
3146    {ok, Data1} = ?FILE_MODULE:pread(Fd3, bof, Size+1),
3147    ok = ?FILE_MODULE:close(Fd3),
3148    %%
3149    %% The same again, but this time with reason 'kill'.
3150    Child2 = spawn(Fun),
3151    Mref2 = erlang:monitor(process, Child2),
3152    receive
3153        {Child2, wrote} ->
3154            ok;
3155        {'DOWN', Mref2, _, _, _} = Down2a ->
3156            ct:fail(Down2a)
3157    end,
3158    timer:sleep(100), % Just in case the file system is slow
3159    {ok, Fd4} = ?FILE_MODULE:open(File, [read]),
3160    eof = ?FILE_MODULE:read(Fd4, 1),
3161    Child2 ! {Parent, continue, kill},
3162    receive
3163        {'DOWN', Mref2, process, Child2, kill} ->
3164            ok;
3165        {'DOWN', Mref2, _, _, _} = Down2b ->
3166            ct:fail(Down2b)
3167    end,
3168    timer:sleep(100), % Just in case the file system is slow
3169    eof = ?FILE_MODULE:pread(Fd4, bof, 1),
3170    ok  = ?FILE_MODULE:close(Fd4),
3171    %%
3172    %% Test if file position works with delayed_write
3173    {ok, Fd5} = ?FILE_MODULE:open(File, [raw, read, write,
3174					 delayed_write]),
3175    ok = ?FILE_MODULE:truncate(Fd5),
3176    ok = ?FILE_MODULE:write(Fd5, [Data1|Data2]),
3177    {ok, 0} = ?FILE_MODULE:position(Fd5, bof),
3178    ok = ?FILE_MODULE:write(Fd5, [Data3]),
3179    {ok, Data2} = ?FILE_MODULE:read(Fd5, Size+1),
3180    {ok, 0} = ?FILE_MODULE:position(Fd5, bof),
3181    Data3Data2 = Data3++Data2,
3182    {ok, Data3Data2} = ?FILE_MODULE:read(Fd5, 2*Size+1),
3183    ok = ?FILE_MODULE:close(Fd5),
3184    %%
3185    [] = flush(),
3186    ok.
3187
3188
3189%% Tests file:pid2name/1.
3190pid2name(Config) when is_list(Config) ->
3191    RootDir = proplists:get_value(priv_dir, Config),
3192    Base = test_server:temp_name(
3193	     filename:join(RootDir, "pid2name_")),
3194    Name1 = [Base, '.txt'],
3195    Name2 = Base ++ ".txt",
3196    %%
3197    {ok, Pid} = file:open(Name1, [write]),
3198    {ok, Name2} = file:pid2name(Pid),
3199    undefined = file:pid2name(self()),
3200    ok = file:close(Pid),
3201    ct:sleep(1000),
3202    false = is_process_alive(Pid),
3203    undefined = file:pid2name(Pid),
3204    ok.
3205
3206
3207%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3208
3209%% Tests the file open option {read_ahead, Size}.
3210
3211read_ahead(Config) when is_list(Config) ->
3212    RootDir = proplists:get_value(priv_dir, Config),
3213    File = filename:join(RootDir,
3214			 atom_to_list(?MODULE)++"_read_ahead.txt"),
3215    Data1 = "asdfghjkl", % Must be
3216    Data2 = "qwertyuio", % same
3217    Data3 = "zxcvbnm,.", % length
3218    Size = length(Data1),
3219    Size = length(Data2),
3220    Size = length(Data3),
3221    %%
3222    %% Test caching of normal non-raw file
3223    {ok, Fd1} = ?FILE_MODULE:open(File, [write]),
3224    ok = ?FILE_MODULE:write(Fd1, [Data1|Data1]),
3225    timer:sleep(1000), % Just in case the file system is slow
3226    {ok, Fd2} = ?FILE_MODULE:open(File, [read, {read_ahead, 2*Size}]),
3227    {ok, Data1} = ?FILE_MODULE:read(Fd2, Size),
3228    ok = ?FILE_MODULE:pwrite(Fd1, Size, Data2),
3229    timer:sleep(1000), % Just in case the file system is slow
3230    {ok, Data1} = ?FILE_MODULE:read(Fd2, Size), % Will read cached data
3231    Data2Data2Data2 = Data2++Data2++Data2,
3232    ok = ?FILE_MODULE:pwrite(Fd1, eof, Data2Data2Data2),
3233    timer:sleep(1000), % Just in case the file system is slow
3234    {ok, Data2Data2Data2} =
3235	?FILE_MODULE:read(Fd2, 3*Size), % Read more than cache buffer
3236    ok = ?FILE_MODULE:close(Fd1),
3237    ok = ?FILE_MODULE:close(Fd2),
3238    %% Test caching of raw file and default parameters
3239    {ok, Fd3} = ?FILE_MODULE:open(File, [raw, write]),
3240    ok = ?FILE_MODULE:write(Fd3, [Data1|Data1]),
3241    timer:sleep(1000), % Just in case the file system is slow
3242    {ok, Fd4} = ?FILE_MODULE:open(File, [raw, read, read_ahead]),
3243    {ok, Data1} = ?FILE_MODULE:read(Fd4, Size),
3244    ok = ?FILE_MODULE:pwrite(Fd3, Size, Data2),
3245    timer:sleep(1000), % Just in case the file system is slow
3246    {ok, Data1} = ?FILE_MODULE:read(Fd4, Size), % Will read cached data
3247    ok = ?FILE_MODULE:close(Fd3),
3248    ok = ?FILE_MODULE:close(Fd4),
3249    %% Test if the file position works in combination with read_ahead
3250    {ok, Fd5} = ?FILE_MODULE:open(File, [raw, read, write, read_ahead]),
3251    ok = ?FILE_MODULE:truncate(Fd5),
3252    ok = ?FILE_MODULE:write(Fd5, [Data1,Data1|Data3]),
3253    {ok, 0} = ?FILE_MODULE:position(Fd5, bof),
3254    {ok, Data1} = ?FILE_MODULE:read(Fd5, Size),
3255    ok = ?FILE_MODULE:write(Fd5, Data2),
3256    {ok, 0} = ?FILE_MODULE:position(Fd5, bof),
3257    Data1Data2Data3 = Data1++Data2++Data3,
3258    {ok, Data1Data2Data3} = ?FILE_MODULE:read(Fd5, 3*Size+1),
3259    ok = ?FILE_MODULE:close(Fd5),
3260
3261    %% Ensure that a read that draws from both the buffer and the file won't
3262    %% return anything wonky.
3263    SplitData = << <<(I rem 256)>> || I <- lists:seq(1, 1024) >>,
3264    file:write_file(File, SplitData),
3265    {ok, Fd6} = ?FILE_MODULE:open(File, [raw, read, binary, {read_ahead, 256}]),
3266    {ok, <<1>>} = file:read(Fd6, 1),
3267    <<1, Shifted:512/binary, _Rest/binary>> = SplitData,
3268    {ok, Shifted} = file:read(Fd6, 512),
3269
3270    %%
3271    [] = flush(),
3272    ok.
3273
3274
3275
3276%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3277
3278
3279
3280%% Tests the segmenting of large reads.
3281segment_read(Config) when is_list(Config) ->
3282    Name = filename:join(proplists:get_value(priv_dir, Config),
3283			 ?MODULE_STRING ++ "_segment_read"),
3284    SegSize = 256*1024,
3285    SegCnt = SegSize div 4,
3286    Cnt = 4 * SegCnt,
3287    ok = create_file(Name, Cnt),
3288    %%
3289    %% read_file/1
3290    %%
3291    {ok, Bin} = ?FILE_MODULE:read_file(Name),
3292    true = verify_bin(Bin, 0, Cnt),
3293    %%
3294    %% read/2
3295    %%
3296    %% Not segmented
3297    {ok, FD1} = ?FILE_MODULE:open(Name, [read, raw, binary]),
3298    {ok, B1a} = ?FILE_MODULE:read(FD1, SegSize),
3299    {ok, B1b} = ?FILE_MODULE:read(FD1, SegSize),
3300    {ok, B1c} = ?FILE_MODULE:read(FD1, SegSize),
3301    {ok, B1d} = ?FILE_MODULE:read(FD1, SegSize),
3302    ok = ?FILE_MODULE:close(FD1),
3303    true = verify_bin(B1a, 0*SegCnt, SegCnt),
3304    true = verify_bin(B1b, 1*SegCnt, SegCnt),
3305    true = verify_bin(B1c, 2*SegCnt, SegCnt),
3306    true = verify_bin(B1d, 3*SegCnt, SegCnt),
3307    %%
3308    %% Segmented
3309    {ok, FD2} = ?FILE_MODULE:open(Name, [read, raw, binary]),
3310    {ok, B2a} = ?FILE_MODULE:read(FD2, 1*SegSize),
3311    {ok, B2b} = ?FILE_MODULE:read(FD2, 2*SegSize),
3312    {ok, B2c} = ?FILE_MODULE:read(FD2, 2*SegSize),
3313    ok = ?FILE_MODULE:close(FD2),
3314    true = verify_bin(B2a, 0*SegCnt, 1*SegCnt),
3315    true = verify_bin(B2b, 1*SegCnt, 2*SegCnt),
3316    true = verify_bin(B2c, 3*SegCnt, 1*SegCnt),
3317    %%
3318    %% pread/3
3319    %%
3320    {ok, FD3} = ?FILE_MODULE:open(Name, [read, raw, binary]),
3321    %%
3322    %% Not segmented
3323    {ok, B3d} = ?FILE_MODULE:pread(FD3, 3*SegSize, SegSize),
3324    {ok, B3c} = ?FILE_MODULE:pread(FD3, 2*SegSize, SegSize),
3325    {ok, B3b} = ?FILE_MODULE:pread(FD3, 1*SegSize, SegSize),
3326    {ok, B3a} = ?FILE_MODULE:pread(FD3, 0*SegSize, SegSize),
3327    true = verify_bin(B3a, 0*SegCnt, SegCnt),
3328    true = verify_bin(B3b, 1*SegCnt, SegCnt),
3329    true = verify_bin(B3c, 2*SegCnt, SegCnt),
3330    true = verify_bin(B3d, 3*SegCnt, SegCnt),
3331    %%
3332    %% Segmented
3333    {ok, B3g} = ?FILE_MODULE:pread(FD3, 3*SegSize, 2*SegSize),
3334    {ok, B3f} = ?FILE_MODULE:pread(FD3, 1*SegSize, 2*SegSize),
3335    {ok, B3e} = ?FILE_MODULE:pread(FD3, 0*SegSize, 1*SegSize),
3336    true = verify_bin(B3e, 0*SegCnt, 1*SegCnt),
3337    true = verify_bin(B3f, 1*SegCnt, 2*SegCnt),
3338    true = verify_bin(B3g, 3*SegCnt, 1*SegCnt),
3339    %%
3340    ok = ?FILE_MODULE:close(FD3),
3341    %%
3342    %% pread/2
3343    %%
3344    {ok, FD5} = ?FILE_MODULE:open(Name, [read, raw, binary]),
3345    %%
3346    %% +---+---+---+---+
3347    %% | 4 | 3 | 2 | 1 |
3348    %% +---+---+---+---+
3349    %% <       ^       >
3350    {ok, [B5d, B5c, B5b, B5a]} =
3351	?FILE_MODULE:pread(FD5, [{3*SegSize, SegSize},
3352				 {2*SegSize, SegSize},
3353				 {1*SegSize, SegSize},
3354				 {0*SegSize, SegSize}]),
3355    true = verify_bin(B5a, 0*SegCnt, SegCnt),
3356    true = verify_bin(B5b, 1*SegCnt, SegCnt),
3357    true = verify_bin(B5c, 2*SegCnt, SegCnt),
3358    true = verify_bin(B5d, 3*SegCnt, SegCnt),
3359    %%
3360    %% +---+-------+-------+
3361    %% | 3 |   2   |   1   |
3362    %% +---+-------+-------+
3363    %% <     ^     ^   >
3364    {ok, [B5g, B5f, B5e]} =
3365	?FILE_MODULE:pread(FD5, [{3*SegSize, 2*SegSize},
3366				 {1*SegSize, 2*SegSize},
3367				 {0*SegSize, 1*SegSize}]),
3368    true = verify_bin(B5e, 0*SegCnt, 1*SegCnt),
3369    true = verify_bin(B5f, 1*SegCnt, 2*SegCnt),
3370    true = verify_bin(B5g, 3*SegCnt, 1*SegCnt),
3371    %%
3372    %%
3373    %% +-------+-----------+
3374    %% |   2   |     1     |
3375    %% +-------+-----------+
3376    %% <     ^     ^   >
3377    {ok, [B5i, B5h]} =
3378	?FILE_MODULE:pread(FD5, [{2*SegSize, 3*SegSize},
3379				 {0*SegSize, 2*SegSize}]),
3380    true = verify_bin(B5h, 0*SegCnt, 2*SegCnt),
3381    true = verify_bin(B5i, 2*SegCnt, 2*SegCnt),
3382    %%
3383    %% +-------+---+---+
3384    %% |   3   | 2 | 1 |
3385    %% +-------+---+---+
3386    %% <     ^     ^   >
3387    {ok, [B5l, B5k, B5j]} =
3388	?FILE_MODULE:pread(FD5, [{3*SegSize, 1*SegSize},
3389				 {2*SegSize, 1*SegSize},
3390				 {0*SegSize, 2*SegSize}]),
3391    true = verify_bin(B5j, 0*SegCnt, 2*SegCnt),
3392    true = verify_bin(B5k, 2*SegCnt, 1*SegCnt),
3393    true = verify_bin(B5l, 3*SegCnt, 1*SegCnt),
3394    %%
3395    %% Real time response time test.
3396    %%
3397    Req = lists:flatten(lists:duplicate(17,
3398					[{2*SegSize, 2*SegSize},
3399					 {0*SegSize, 2*SegSize}])),
3400    {{ok, _}, Comment} =
3401	response_analysis(?FILE_MODULE, pread, [FD5, Req]),
3402    ok = ?FILE_MODULE:close(FD5),
3403    %%
3404    [] = flush(),
3405    {comment, Comment}.
3406
3407
3408
3409%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3410
3411
3412
3413%% Tests the segmenting of large writes.
3414segment_write(Config) when is_list(Config) ->
3415    Name = filename:join(proplists:get_value(priv_dir, Config),
3416			 ?MODULE_STRING ++ "_segment_write"),
3417    SegSize = 256*1024,
3418    SegCnt = SegSize div 4,
3419    Cnt = 4 * SegCnt,
3420    Bin = create_bin(0, Cnt),
3421    %%
3422    %% write/2
3423    %%
3424    %% Not segmented
3425    {ok, FD1} = ?FILE_MODULE:open(Name, [write, raw, binary]),
3426    ok = ?FILE_MODULE:write(FD1, subbin(Bin, 0*SegSize, 1*SegSize)),
3427    ok = ?FILE_MODULE:write(FD1, subbin(Bin, 1*SegSize, 1*SegSize)),
3428    ok = ?FILE_MODULE:write(FD1, subbin(Bin, 2*SegSize, 1*SegSize)),
3429    ok = ?FILE_MODULE:write(FD1, subbin(Bin, 3*SegSize, 1*SegSize)),
3430    ok = ?FILE_MODULE:close(FD1),
3431    true = verify_file(Name, Cnt),
3432    %%
3433    %% Segmented
3434    {ok, FD2} = ?FILE_MODULE:open(Name, [write, raw, binary]),
3435    ok = ?FILE_MODULE:write(FD2, subbin(Bin, 0*SegSize, 1*SegSize)),
3436    ok = ?FILE_MODULE:write(FD2, subbin(Bin, 1*SegSize, 2*SegSize)),
3437    ok = ?FILE_MODULE:write(FD2, subbin(Bin, 3*SegSize, 1*SegSize)),
3438    ok = ?FILE_MODULE:close(FD2),
3439    true = verify_file(Name, Cnt),
3440    %%
3441    %% +---+---+---+---+
3442    %% |   |   |   |   |
3443    %% +---+---+---+---+
3444    %% <       ^       >
3445    ok = write_file(Name, [subbin(Bin, 0*SegSize, 1*SegSize),
3446			   subbin(Bin, 1*SegSize, 1*SegSize),
3447			   subbin(Bin, 2*SegSize, 1*SegSize),
3448			   subbin(Bin, 3*SegSize, 1*SegSize)]),
3449    true = verify_file(Name, Cnt),
3450    %%
3451    %% +---+-------+---+
3452    %% |   |       |   |
3453    %% +---+-------+---+
3454    %% <     ^     ^   >
3455    ok = write_file(Name, [subbin(Bin, 0*SegSize, 1*SegSize),
3456			   subbin(Bin, 1*SegSize, 2*SegSize),
3457			   subbin(Bin, 3*SegSize, 1*SegSize)]),
3458    true = verify_file(Name, Cnt),
3459    %%
3460    %% +-------+-------+
3461    %% |       |       |
3462    %% +-------+-------+
3463    %% <     ^     ^   >
3464    ok = write_file(Name, [subbin(Bin, 0*SegSize, 2*SegSize),
3465			   subbin(Bin, 2*SegSize, 2*SegSize)]),
3466    true = verify_file(Name, Cnt),
3467    %%
3468    %% +-------+---+---+
3469    %% |       |   |   |
3470    %% +-------+---+---+
3471    %% <     ^     ^   >
3472    ok = write_file(Name, [subbin(Bin, 0*SegSize, 2*SegSize),
3473			   subbin(Bin, 2*SegSize, 1*SegSize),
3474			   subbin(Bin, 3*SegSize, 1*SegSize)]),
3475    true = verify_file(Name, Cnt),
3476    %%
3477    %% pwrite/3
3478    %%
3479    %% Not segmented
3480    {ok, FD3} = ?FILE_MODULE:open(Name, [write, raw, binary]),
3481    ok = ?FILE_MODULE:pwrite(FD3, 3*SegSize,
3482			     subbin(Bin, 3*SegSize, 1*SegSize)),
3483    ok = ?FILE_MODULE:pwrite(FD3, 2*SegSize,
3484			     subbin(Bin, 2*SegSize, 1*SegSize)),
3485    ok = ?FILE_MODULE:pwrite(FD3, 1*SegSize,
3486			     subbin(Bin, 1*SegSize, 1*SegSize)),
3487    ok = ?FILE_MODULE:pwrite(FD3, 0*SegSize,
3488			     subbin(Bin, 0*SegSize, 1*SegSize)),
3489    ok = ?FILE_MODULE:close(FD3),
3490    true = verify_file(Name, Cnt),
3491    %%
3492    %% Segmented
3493    {ok, FD4} = ?FILE_MODULE:open(Name, [write, raw, binary]),
3494    ok = ?FILE_MODULE:pwrite(FD4, 3*SegSize,
3495			     subbin(Bin, 3*SegSize, 1*SegSize)),
3496    ok = ?FILE_MODULE:pwrite(FD4, 1*SegSize,
3497			     subbin(Bin, 1*SegSize, 2*SegSize)),
3498    ok = ?FILE_MODULE:pwrite(FD4, 0*SegSize,
3499			     subbin(Bin, 0*SegSize, 1*SegSize)),
3500    ok = ?FILE_MODULE:close(FD4),
3501    true = verify_file(Name, Cnt),
3502
3503
3504
3505    %%
3506    %% pwrite/2
3507    %%
3508    %% Not segmented
3509    {ok, FD5} = ?FILE_MODULE:open(Name, [write, raw, binary]),
3510    ok = ?FILE_MODULE:pwrite(FD5, [{3*SegSize,
3511				    subbin(Bin, 3*SegSize, 1*SegSize)}]),
3512    ok = ?FILE_MODULE:pwrite(FD5, [{2*SegSize,
3513				    subbin(Bin, 2*SegSize, 1*SegSize)}]),
3514    ok = ?FILE_MODULE:pwrite(FD5, [{1*SegSize,
3515				    subbin(Bin, 1*SegSize, 1*SegSize)}]),
3516    ok = ?FILE_MODULE:pwrite(FD5, [{0*SegSize,
3517				    subbin(Bin, 0*SegSize, 1*SegSize)}]),
3518    ok = ?FILE_MODULE:close(FD5),
3519    true = verify_file(Name, Cnt),
3520    %%
3521    %% Segmented
3522    {ok, FD6} = ?FILE_MODULE:open(Name, [write, raw, binary]),
3523    ok = ?FILE_MODULE:pwrite(FD6, [{3*SegSize,
3524				    subbin(Bin, 3*SegSize, 1*SegSize)}]),
3525    ok = ?FILE_MODULE:pwrite(FD6, [{1*SegSize,
3526				    subbin(Bin, 1*SegSize, 2*SegSize)}]),
3527    ok = ?FILE_MODULE:pwrite(FD6, [{0*SegSize,
3528				    subbin(Bin, 0*SegSize, 1*SegSize)}]),
3529    ok = ?FILE_MODULE:close(FD6),
3530    true = verify_file(Name, Cnt),
3531    %%
3532    %% +---+---+---+---+
3533    %% | 4 | 3 | 2 | 1 |
3534    %% +---+---+---+---+
3535    %% <       ^       >
3536    ok = pwrite_file(Name, [{3*SegSize,
3537			     subbin(Bin, 3*SegSize, 1*SegSize)},
3538			    {2*SegSize,
3539			     subbin(Bin, 2*SegSize, 1*SegSize)},
3540			    {1*SegSize,
3541			     subbin(Bin, 1*SegSize, 1*SegSize)},
3542			    {0*SegSize,
3543			     subbin(Bin, 0*SegSize, 1*SegSize)}]),
3544    true = verify_file(Name, Cnt),
3545    %%
3546    %% +---+-------+---+
3547    %% | 3 |   2   | 1 |
3548    %% +---+-------+---+
3549    %% <     ^     ^   >
3550    ok = pwrite_file(Name, [{3*SegSize,
3551			     subbin(Bin, 3*SegSize, 1*SegSize)},
3552			    {1*SegSize,
3553			     subbin(Bin, 1*SegSize, 2*SegSize)},
3554			    {0*SegSize,
3555			     subbin(Bin, 0*SegSize, 1*SegSize)}]),
3556    true = verify_file(Name, Cnt),
3557    %%
3558    %% +-------+-------+
3559    %% |   2   |   1   |
3560    %% +-------+-------+
3561    %% <     ^     ^   >
3562    ok = pwrite_file(Name, [{2*SegSize,
3563			     subbin(Bin, 2*SegSize, 2*SegSize)},
3564			    {0*SegSize,
3565			     subbin(Bin, 0*SegSize, 2*SegSize)}]),
3566    true = verify_file(Name, Cnt),
3567    %%
3568    %% +-------+---+---+
3569    %% |   3   | 2 | 1 |
3570    %% +-------+---+---+
3571    %% <     ^     ^   >
3572    ok = pwrite_file(Name, [{3*SegSize,
3573			     subbin(Bin, 3*SegSize, 1*SegSize)},
3574			    {2*SegSize,
3575			     subbin(Bin, 2*SegSize, 1*SegSize)},
3576			    {0*SegSize,
3577			     subbin(Bin, 0*SegSize, 2*SegSize)}]),
3578    true = verify_file(Name, Cnt),
3579    %%
3580    %% Real time response time test.
3581    %%
3582    {ok, FD7} = ?FILE_MODULE:open(Name, [write, raw, binary]),
3583    Req = lists:flatten(lists:duplicate(17,
3584					[{2*SegSize,
3585					  subbin(Bin, 2*SegSize, 2*SegSize)},
3586					 {0*SegSize,
3587					  subbin(Bin, 0*SegSize, 2*SegSize)}])),
3588    {ok, Comment} =
3589	response_analysis(?FILE_MODULE, pwrite, [FD7, Req]),
3590    ok = ?FILE_MODULE:close(FD7),
3591    %%
3592    [] = flush(),
3593    {comment, Comment}.
3594
3595
3596%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3597
3598%% Test Dets special indirect pread.
3599ipread(Config) when is_list(Config) ->
3600    Dir = proplists:get_value(priv_dir, Config),
3601    ok = ipread_int(Dir, [raw, binary]),
3602    ok = ipread_int(Dir, [raw]),
3603    ok = ipread_int(Dir, [binary]),
3604    ok = ipread_int(Dir, []),
3605    ok = ipread_int(Dir, [ram, binary]),
3606    ok = ipread_int(Dir, [ram]),
3607    %%
3608    [] = flush(),
3609    ok.
3610
3611ipread_int(Dir, ModeList) ->
3612    Name =
3613	filename:join(Dir,
3614		      lists:flatten([?MODULE_STRING, "_ipread",
3615				     lists:map(fun (X) ->
3616						       ["_", atom_to_list(X)]
3617					       end,
3618					       ModeList)])),
3619    io:format("ipread_int<~p, ~p>~n", [Name, ModeList]),
3620    {Conv, Sizeof} =
3621	case lists:member(binary, ModeList) of
3622	    true ->
3623		{fun (Bin) when is_binary(Bin) -> Bin;
3624		     (List) when is_list(List) -> list_to_binary(List)
3625		 end,
3626		 fun erlang:byte_size/1};
3627	    false ->
3628		{fun (Bin) when is_binary(Bin) -> binary_to_list(Bin);
3629		     (List) when is_list(List) -> List
3630		 end,
3631		 fun erlang:length/1}
3632	end,
3633    Pos = 4711,
3634    Data = Conv("THE QUICK BROWN FOX JUMPS OVER A LAZY DOG"),
3635    Size = Sizeof(Data),
3636    Init = Conv("                 "),
3637    SizeInit = Sizeof(Init),
3638    Head = Conv(<<Size:32/big-unsigned, Pos:32/big-unsigned>>),
3639    Filler = Conv(bytes($ , Pos-SizeInit-Sizeof(Head))),
3640    Size1 = Size+1,
3641    SizePos = Size+Pos,
3642    %%
3643    {ok, FD} = ?FILE_MODULE:open(Name, [write, read | ModeList]),
3644    ok = ?FILE_MODULE:truncate(FD),
3645    ok = ?FILE_MODULE:write(FD, Init),
3646    ok = ?FILE_MODULE:write(FD, Head),
3647    ok = ?FILE_MODULE:write(FD, Filler),
3648    ok = ?FILE_MODULE:write(FD, Data),
3649    %% Correct read
3650    {ok, {Size, Pos, Data}} =
3651	?FILE_MODULE:ipread_s32bu_p32bu(FD, SizeInit, infinity),
3652    %% Invalid header - size > max
3653    eof =
3654	?FILE_MODULE:ipread_s32bu_p32bu(FD, SizeInit, Size-1),
3655    %% Data block protudes over eof
3656    ok =
3657	?FILE_MODULE:pwrite(FD, SizeInit,
3658			    <<Size1:32/big-unsigned,
3659			      Pos:32/big-unsigned>>),
3660    {ok, {Size1, Pos, Data}} =
3661	?FILE_MODULE:ipread_s32bu_p32bu(FD, SizeInit, Size1),
3662    %% Data block outside file
3663    ok =
3664	?FILE_MODULE:pwrite(FD, SizeInit,
3665			    <<Size:32/big-unsigned,
3666			      SizePos:32/big-unsigned>>),
3667    {ok, {Size, SizePos, eof}} =
3668	?FILE_MODULE:ipread_s32bu_p32bu(FD, SizeInit, Size),
3669    %% Zero size
3670    ok =
3671	?FILE_MODULE:pwrite(FD, SizeInit,
3672			    <<0:32/big-unsigned,
3673			      Pos:32/big-unsigned>>),
3674    {ok, {0, Pos, eof}} =
3675	?FILE_MODULE:ipread_s32bu_p32bu(FD, SizeInit, Size),
3676    %% Invalid header - protudes over eof
3677    eof =
3678	?FILE_MODULE:ipread_s32bu_p32bu(FD,
3679					Pos+Size-(Sizeof(Head)-1),
3680					infinity),
3681    %% Header not even in file
3682    eof =
3683	?FILE_MODULE:ipread_s32bu_p32bu(FD, Pos+Size, infinity),
3684    %%
3685    ok = ?FILE_MODULE:close(FD),
3686    ok.
3687
3688
3689
3690%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3691
3692%% Tests interleaved read and writes.
3693interleaved_read_write(Config) when is_list(Config) ->
3694    Dir = proplists:get_value(priv_dir, Config),
3695    File =
3696	filename:join(Dir, ?MODULE_STRING++"interleaved_read_write.txt"),
3697    {ok,F1} = ?FILE_MODULE:open(File, [write]),
3698    ok = ?FILE_MODULE:write(F1, "data---r1."), % 10 chars each
3699    ok = ?FILE_MODULE:write(F1, "data---r2."),
3700    ok = ?FILE_MODULE:write(F1, "data---r3."),
3701    ok = ?FILE_MODULE:close(F1),
3702    {ok,F2} = ?FILE_MODULE:open(File, [read, write]),
3703    {ok, "data---r1."} = ?FILE_MODULE:read(F2, 10),
3704    ok = ?FILE_MODULE:write(F2, "data---w2."),
3705    ok = ?FILE_MODULE:close(F2),
3706    {ok,F3} = ?FILE_MODULE:open(File, [read]),
3707    {ok, "data---r1."} = ?FILE_MODULE:read(F3, 10),
3708    {ok, "data---w2."} = ?FILE_MODULE:read(F3, 10),
3709    {ok, "data---r3."} = ?FILE_MODULE:read(F3, 10),
3710    eof = ?FILE_MODULE:read(F3, 1),
3711    ok = ?FILE_MODULE:close(F2),
3712    %%
3713    [] = flush(),
3714    ok.
3715
3716%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3717
3718%% OTP-5814. eval/consult/script return correct line numbers.
3719otp_5814(Config) when is_list(Config) ->
3720    PrivDir = proplists:get_value(priv_dir, Config),
3721    File = filename:join(PrivDir, "otp_5814"),
3722    Path = [PrivDir],
3723    ok = file:write_file(File, <<"{a,b,c}.
3724                                        a.
3725b.
3726c.
3727{d,e,
3728 [}.">>),
3729    {error, {6,erl_parse,_}} = file:eval(File),
3730 {error, {6,erl_parse,_}} = file:consult(File),
3731 {error, {6,erl_parse,_}} = file:path_consult(Path, File),
3732 {error, {6,erl_parse,_}} = file:path_eval(Path, File),
3733 {error, {6,erl_parse,_}} = file:script(File),
3734 {error, {6,erl_parse,_}} = file:path_script(Path, File),
3735
3736 ok = file:write_file(File, <<>>),
3737 {error, {1,file,undefined_script}} = file:path_script(Path, File),
3738
3739    %% The error is not propagated...
3740 ok = file:write_file(File, <<"a.
3741                                        b.
37421/0.">>),
3743    {error, {3, file, {error, badarith, _}}} = file:eval(File),
3744
3745ok = file:write_file(File, <<"erlang:raise(throw, apa, []).">>),
3746{error, {1, file, {throw, apa, _}}} = file:eval(File),
3747
3748file:delete(File),
3749ok.
3750
3751%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3752
3753%% OTP-10852. +fnu and latin1 filenames.
3754otp_10852(Config) when is_list(Config) ->
3755    Node = start_node(erl_pp_helper, "+fnu"),
3756    Dir = proplists:get_value(priv_dir, Config),
3757    B = filename:join(Dir, <<"\xE4">>),
3758    ok = rpc_call(Node, get_cwd, [B]),
3759    {error, no_translation} = rpc_call(Node, set_cwd, [B]),
3760    ok = rpc_call(Node, delete, [B]),
3761    ok = rpc_call(Node, rename, [B, B]),
3762    ok = rpc_call(Node, read_file_info, [B]),
3763    ok = rpc_call(Node, read_link_info, [B]),
3764    ok = rpc_call(Node, read_link, [B]),
3765    ok = rpc_call(Node, write_file_info, [B,#file_info{}]),
3766    ok = rpc_call(Node, list_dir, [B]),
3767    ok = rpc_call(Node, list_dir_all, [B]),
3768    ok = rpc_call(Node, read_file, [B]),
3769    ok = rpc_call(Node, make_link, [B,B]),
3770    case rpc_call(Node, make_symlink, [B,B]) of
3771        {error, eilseq} ->
3772            %% Some versions of OS X refuse to create files with illegal names.
3773            {unix,darwin} = os:type();
3774        {error, eperm} ->
3775            %% The test user might not have permission to create symlinks.
3776            {win32,_} = os:type();
3777        ok ->
3778            ok
3779    end,
3780    ok = rpc_call(Node, delete, [B]),
3781    case rpc_call(Node, make_dir, [B]) of
3782        {error, eilseq} ->
3783            {unix,darwin} = os:type();
3784        ok ->
3785            ok
3786    end,
3787    ok = rpc_call(Node, del_dir, [B]),
3788    case rpc_call(Node, write_file, [B,B]) of
3789        {error, eilseq} ->
3790            {unix,darwin} = os:type();
3791        ok ->
3792            {ok, Fd} = rpc_call(Node, open, [B,[read]]),
3793            ok = rpc_call(Node, close, [Fd]),
3794            {ok,0} = rpc_call(Node, copy, [B,B]),
3795            {ok, Fd2, B} = rpc_call(Node, path_open, [["."], B, [read]]),
3796            ok = rpc_call(Node, close, [Fd2])
3797    end,
3798    true = test_server:stop_node(Node),
3799    ok.
3800
3801rpc_call(N, F, As) ->
3802    case rpc:call(N, ?FILE_MODULE, F, As) of
3803        {error, enotsup} -> ok;
3804        {error, enoent} -> ok;
3805        {error, badarg} -> ok;
3806        Else -> Else
3807    end.
3808
3809%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3810
3811large_file() ->
3812    [{timetrap,{minutes,20}}].
3813
3814%% Tests positioning in large files (> 4G).
3815large_file(Config) when is_list(Config) ->
3816    run_large_file_test(Config,
3817			fun(Name) -> do_large_file(Name) end,
3818			"_large_file").
3819
3820do_large_file(Name) ->
3821    S = "1234567890",
3822    L = length(S),
3823    R = lists:reverse(S),
3824    P = 1 bsl 32,
3825    Ss = lists:sort(S),
3826    Rs = lists:reverse(Ss),
3827    {ok,F}  = ?FILE_MODULE:open(Name, [raw,read,write]),
3828    ok      = ?FILE_MODULE:write(F, S),
3829    {ok,P}  = ?FILE_MODULE:position(F, P),
3830    ok      = ?FILE_MODULE:write(F, R),
3831    {ok,0}  = ?FILE_MODULE:position(F, bof),
3832    {ok,S}  = ?FILE_MODULE:read(F, L),
3833    {ok,P}  = ?FILE_MODULE:position(F, {eof,-L}),
3834    {ok,R}  = ?FILE_MODULE:read(F, L+1),
3835    {ok,S}  = ?FILE_MODULE:pread(F, 0, L),
3836    {ok,R}  = ?FILE_MODULE:pread(F, P, L+1),
3837    ok      = ?FILE_MODULE:pwrite(F, 0, Ss),
3838    ok      = ?FILE_MODULE:pwrite(F, P, Rs),
3839    {ok,0}  = ?FILE_MODULE:position(F, bof),
3840    {ok,Ss} = ?FILE_MODULE:read(F, L),
3841    {ok,P}  = ?FILE_MODULE:position(F, {eof,-L}),
3842    {ok,Rs} = ?FILE_MODULE:read(F, L+1),
3843    ok      = ?FILE_MODULE:close(F),
3844    %% Reopen the file with 'append'; used to fail on Windows causing
3845    %% writes to go to the beginning of the file for files > 4GB.
3846    PL = P + L,
3847    PLL = PL + L,
3848    {ok,F1}  = ?FILE_MODULE:open(Name, [raw,read,write,append]),
3849    ok       = ?FILE_MODULE:write(F1, R),
3850    {ok,PLL} = ?FILE_MODULE:position(F1, {cur,0}),
3851    {ok,Rs}  = ?FILE_MODULE:pread(F1, P, L),
3852    {ok,PL}  = ?FILE_MODULE:position(F1, {eof,-L}),
3853    {ok,R}   = ?FILE_MODULE:read(F1, L+1),
3854    ok       = ?FILE_MODULE:close(F1),
3855
3856    ok.
3857
3858%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3859
3860large_write() ->
3861    [{timetrap,{minutes,20}}].
3862
3863large_write(Config) when is_list(Config) ->
3864    run_large_file_test(Config,
3865			fun(Name) -> do_large_write(Name) end,
3866			"_large_write").
3867
3868do_large_write(Name) ->
3869    Memsize = memsize(),
3870    io:format("Memsize = ~w Bytes~n", [Memsize]),
3871    case {erlang:system_info(wordsize),Memsize} of
3872	{4,_} ->
3873	    {skip,"Needs a 64-bit emulator"};
3874	{8,N} when N < 6 bsl 30 ->
3875	    {skip,
3876	     "This machine has < 6 GB  memory: "
3877	     ++integer_to_list(N)};
3878	{8,_} ->
3879	    Size = 4*1024*1024*1024+1,
3880	    Bin = <<0:Size/unit:8>>,
3881	    ok = file:write_file(Name, Bin),
3882	    {ok,#file_info{size=Size}} = file:read_file_info(Name),
3883	    ok
3884    end.
3885
3886%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3887
3888%% Benchmarks
3889%%
3890%% Note that we only measure the time it takes to run the isolated file
3891%% operations and that the actual test runtime can differ significantly,
3892%% especially on the write side as the files need to be truncated before
3893%% writing.
3894
3895large_writes(Config) when is_list(Config) ->
3896    Modes = [raw, binary],
3897    OpCount = 4096,
3898    Data = <<0:(64 bsl 10)/unit:8>>,
3899    run_write_benchmark(Config, Modes, OpCount, Data).
3900
3901large_writes_delayed(Config) when is_list(Config) ->
3902    %% Each write is exactly as large as the delay buffer, causing the writes
3903    %% to pass through each time, giving us a decent idea of how much overhead
3904    %% delayed_write adds.
3905    Modes = [raw, binary, {delayed_write, 64 bsl 10, 2000}],
3906    OpCount = 4096,
3907    Data = <<0:(64 bsl 10)/unit:8>>,
3908    run_write_benchmark(Config, Modes, OpCount, Data).
3909
3910tiny_writes(Config) when is_list(Config) ->
3911    Modes = [raw, binary],
3912    OpCount = 512 bsl 10,
3913    Data = <<0>>,
3914    run_write_benchmark(Config, Modes, OpCount, Data).
3915
3916tiny_writes_delayed(Config) when is_list(Config) ->
3917    Modes = [raw, binary, {delayed_write, 512 bsl 10, 2000}],
3918    OpCount = 512 bsl 10,
3919    Data = <<0>>,
3920    run_write_benchmark(Config, Modes, OpCount, Data).
3921
3922%% The read benchmarks assume that "benchmark_scratch_file" has been filled by
3923%% the write benchmarks.
3924
3925tiny_reads(Config) when is_list(Config) ->
3926    Modes = [raw, binary],
3927    OpCount = 512 bsl 10,
3928    run_read_benchmark(Config, Modes, OpCount, 1).
3929
3930tiny_reads_ahead(Config) when is_list(Config) ->
3931    Modes = [raw, binary, {read_ahead, 512 bsl 10}],
3932    OpCount = 512 bsl 10,
3933    run_read_benchmark(Config, Modes, OpCount, 1).
3934
3935run_write_benchmark(Config, Modes, OpCount, Data) ->
3936    run_benchmark(Config, [write | Modes], OpCount, fun file:write/2, Data).
3937
3938run_read_benchmark(Config, Modes, OpCount, OpSize) ->
3939    run_benchmark(Config, [read | Modes], OpCount, fun file:read/2, OpSize).
3940
3941run_benchmark(Config, Modes, OpCount, Fun, Arg) ->
3942    ScratchDir = proplists:get_value(priv_dir, Config),
3943    Path = filename:join(ScratchDir, "benchmark_scratch_file"),
3944    {ok, Fd} = file:open(Path, Modes),
3945    submit_throughput_results(Fun, [Fd, Arg], OpCount).
3946
3947submit_throughput_results(Fun, Args, Times) ->
3948    MSecs = measure_repeated_file_op(Fun, Args, Times, millisecond),
3949    IOPS = trunc(Times * (1000 / MSecs)),
3950    ct_event:notify(#event{ name = benchmark_data, data = [{value,IOPS}] }),
3951    {comment, io_lib:format("~p IOPS, ~p ms", [IOPS, trunc(MSecs)])}.
3952
3953measure_repeated_file_op(Fun, Args, Times, Unit) ->
3954    Start = os:perf_counter(Unit),
3955    repeated_apply(Fun, Args, Times),
3956    os:perf_counter(Unit) - Start.
3957
3958repeated_apply(_F, _Args, Times) when Times =< 0 ->
3959    ok;
3960repeated_apply(F, Args, Times) ->
3961    erlang:apply(F, Args),
3962    repeated_apply(F, Args, Times - 1).
3963
3964%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3965
3966
3967response_analysis(Module, Function, Arguments) ->
3968    Parent = self(),
3969    erlang:yield(), % Schedule out before test
3970    Child =
3971	spawn_link(
3972	  fun () ->
3973		  receive {Parent, start, Ts} -> ok end,
3974		  Stat =
3975		      iterate(response_stat(response_stat(init, Ts),
3976					    micro_ts()),
3977			      done,
3978			      fun (S) ->
3979				      erlang:yield(),
3980				      receive
3981					  {Parent, stop} ->
3982					      done
3983				      after 0 ->
3984					      response_stat(S, micro_ts())
3985				      end
3986			      end),
3987		  Parent ! {self(), stopped, response_stat(Stat, micro_ts())}
3988	  end),
3989    Child ! {Parent, start, micro_ts()},
3990    Result = apply(Module, Function, Arguments),
3991    Child ! {Parent, stop},
3992    {N, Sum, _, M, Max} = receive {Child, stopped, X} -> X end,
3993    Mean_ms = (0.001*Sum) / (N-1),
3994    Max_ms = 0.001 * Max,
3995    Comment =
3996	lists:flatten(
3997	  io_lib:format(
3998	    "Scheduling interval: Mean = ~.3f ms, "
3999	    ++"Max = ~.3f ms for no ~p of ~p.~n",
4000	    [Mean_ms, Max_ms, M, (N-1)])),
4001    {Result, Comment}.
4002
4003micro_ts() ->
4004    erlang:monotonic_time(microsecond).
4005
4006response_stat(init, Ts) ->
4007    {0, 0, Ts, 0, 0};
4008response_stat({N, Sum, Ts0, M, Max}, Ts) ->
4009    D = Ts - Ts0,
4010    if D > Max ->
4011	    {N+1, Sum+D, Ts, N, D};
4012       true ->
4013	    {N+1, Sum+D, Ts, M, Max}
4014    end.
4015
4016
4017
4018%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4019
4020
4021
4022%% This function is kept just for benchmarking reasons.
4023%% create_file/2 below is some 44 times faster.
4024
4025create_file_slow(Name, N) when is_integer(N), N >= 0 ->
4026    {ok, FD} =
4027	?FILE_MODULE:open(Name, [raw, write, delayed_write, binary]),
4028    ok = create_file_slow(FD, 0, N),
4029    ok = ?FILE_MODULE:close(FD),
4030    ok.
4031
4032create_file_slow(_FD, M, M) ->
4033    ok;
4034create_file_slow(FD, M, N) ->
4035    ok = ?FILE_MODULE:write(FD, <<M:32/unsigned>>),
4036    create_file_slow(FD, M+1, N).
4037
4038
4039
4040%% Creates a file 'Name' containing 'N' unsigned 32 bit integers
4041%% from 0 to N-1.
4042
4043create_file(Name, N) when is_integer(N), N >= 0 ->
4044    {ok, FD} =
4045	?FILE_MODULE:open(Name, [raw, write, delayed_write, binary]),
4046    ok = create_file(FD, 0, N),
4047    ok = ?FILE_MODULE:close(FD),
4048    ok.
4049
4050create_file(_FD, M, M) ->
4051    ok;
4052create_file(FD, M, N) when M + 1024 =< N ->
4053    create_file(FD, M, M + 1024, []),
4054    create_file(FD, M + 1024, N);
4055create_file(FD, M, N) ->
4056    create_file(FD, M, N, []).
4057
4058create_file(FD, M, M, R) ->
4059    ok = ?FILE_MODULE:write(FD, R);
4060create_file(FD, M, N0, R) when M + 8 =< N0 ->
4061    N1  = N0-1,  N2  = N0-2,  N3  = N0-3,  N4  = N0-4,
4062    N5  = N0-5,  N6  = N0-6,  N7  = N0-7,  N8  = N0-8,
4063    create_file(FD, M, N8,
4064		[<<N8:32/unsigned,  N7:32/unsigned,
4065		   N6:32/unsigned,  N5:32/unsigned,
4066		   N4:32/unsigned,  N3:32/unsigned,
4067		   N2:32/unsigned,  N1:32/unsigned>> | R]);
4068create_file(FD, M, N0, R) ->
4069    N1 = N0-1,
4070    create_file(FD, M, N1, [<<N1:32/unsigned>> | R]).
4071
4072
4073
4074create_bin(M, N) when is_integer(M), is_integer(N), N >= 0, M >= 0 ->
4075    create_bin(M, M+N, []).
4076
4077create_bin(N, N, R) ->
4078    list_to_binary(R);
4079create_bin(M, N0, R) when M+8 =< N0 ->
4080    N1  = N0-1,  N2  = N0-2,  N3  = N0-3,  N4  = N0-4,
4081    N5  = N0-5,  N6  = N0-6,  N7  = N0-7,  N8  = N0-8,
4082    create_bin(M, N8,
4083	       [<<N8:32/unsigned,  N7:32/unsigned,
4084		  N6:32/unsigned,  N5:32/unsigned,
4085		  N4:32/unsigned,  N3:32/unsigned,
4086		  N2:32/unsigned,  N1:32/unsigned>> | R]);
4087create_bin(M, N0, R) ->
4088    N1 = N0-1,
4089    create_bin(M, N1, [<<N1:32/unsigned>> | R]).
4090
4091
4092
4093
4094verify_bin(<<>>, _, 0) ->
4095    true;
4096verify_bin(<<>>, _, _) ->
4097    false;
4098verify_bin(Bin, N, Cnt) ->
4099    N0 = N + 0, N1 = N + 1, N2 = N + 2, N3 = N + 3,
4100    N4 = N + 4, N5 = N + 5, N6 = N + 6, N7 = N + 7,
4101    case Bin of
4102	<<N0:32/unsigned, N1:32/unsigned, N2:32/unsigned, N3:32/unsigned,
4103	  N4:32/unsigned, N5:32/unsigned, N6:32/unsigned, N7:32/unsigned,
4104	  B/binary>> ->
4105	    verify_bin(B, N+8, Cnt-8);
4106	<<N:32/unsigned, B/binary>> ->
4107	    verify_bin(B, N+1, Cnt-1);
4108	_ ->
4109	    false
4110    end.
4111
4112
4113
4114verify_file(Name, N) when is_integer(N), N >= 0 ->
4115    case ?FILE_MODULE:open(Name, [raw, read, binary]) of
4116	{ok, FD} ->
4117	    Result = verify_file(FD, 0, 64*1024, N),
4118	    ok = ?FILE_MODULE:close(FD),
4119	    Result;
4120	Error ->
4121	    Error
4122    end.
4123
4124verify_file(FD, N, _, N) ->
4125    case ?FILE_MODULE:read(FD, 1) of
4126	eof ->
4127	    true;
4128	{ok, _} ->
4129	    false
4130    end;
4131verify_file(FD, M, Cnt, N) when M+Cnt =< N ->
4132    case ?FILE_MODULE:read(FD, 4*Cnt) of
4133	{ok, Bin} ->
4134	    case verify_bin(Bin, M, Cnt) of
4135		true ->
4136		    verify_file(FD, M+Cnt, Cnt, N);
4137		false ->
4138		    false
4139	    end;
4140	_ ->
4141	    false
4142    end;
4143verify_file(FD, M, _Cnt, N) ->
4144    verify_file(FD, M, N-M, N).
4145
4146
4147
4148subbin(Bin, M, N) ->
4149    <<_:M/binary, B:N/binary, _/binary>> = Bin,
4150    B.
4151
4152
4153
4154write_file(Name, Data) ->
4155    case ?FILE_MODULE:open(Name, [raw, write, binary]) of
4156	{ok, FD} ->
4157	    Result = ?FILE_MODULE:write(FD, Data),
4158	    case {Result, ?FILE_MODULE:close(FD)} of
4159		{ok, R} -> R;
4160		_ -> Result
4161	    end;
4162	Error ->
4163	    Error
4164    end.
4165
4166pwrite_file(Name, Data) ->
4167    case ?FILE_MODULE:open(Name, [raw, write, binary]) of
4168	{ok, FD} ->
4169	    Result = ?FILE_MODULE:pwrite(FD, Data),
4170	    case {Result, ?FILE_MODULE:close(FD)} of
4171		{ok, R} -> R;
4172		_ -> Result
4173	    end;
4174	Error ->
4175	    Error
4176    end.
4177
4178%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4179%% Read_line tests
4180%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4181
4182
4183read_line_testdata(PrivDir) ->
4184    All0 = [{fun read_line_create0/1,"Testdata1.txt",5,10},
4185	    {fun read_line_create1/1,"Testdata2.txt",401,802},
4186	    {fun read_line_create2/1,"Testdata3.txt",1,2},
4187	    {fun read_line_create3/1,"Testdata4.txt",601,fail},
4188	    {fun read_line_create4/1,"Testdata5.txt",601,1002},
4189	    {fun read_line_create5/1,"Testdata6.txt",601,1202},
4190	    {fun read_line_create6/1,"Testdata7.txt",601,1202},
4191	    {fun read_line_create7/1,"Testdata8.txt",4001,8002}],
4192    [ {A,filename:join([PrivDir,B]),C,D} || {A,B,C,D} <- All0 ].
4193
4194read_line_create_files(TestData) ->
4195    [ Function(File) || {Function,File,_,_} <- TestData ].
4196
4197read_line_remove_files(TestData) ->
4198    [ file:delete(File) || {_Function,File,_,_} <- TestData ].
4199
4200%% read_line with ?PRIM_FILE.
4201read_line_1(Config) when is_list(Config) ->
4202    PrivDir = proplists:get_value(priv_dir, Config),
4203    All = read_line_testdata(PrivDir),
4204    read_line_create_files(All),
4205    [ begin
4206	  io:format("read_line_all: ~s~n",[File]),
4207	  {X,_} = read_line_all(File),
4208	  true
4209      end || {_,File,X,_} <- All ],
4210    [ begin
4211	  io:format("read_line_all_alternating: ~s~n",[File]),
4212	  {Y,_} = read_line_all_alternating(File),
4213	  true
4214      end || {_,File,_,Y} <- All , Y =/= fail],
4215    [ begin
4216	  io:format("read_line_all_alternating (failing as should): ~s~n",[File]),
4217	  {'EXIT',_} = (catch read_line_all_alternating(File)),
4218	  true
4219      end || {_,File,_,Y} <- All , Y =:= fail],
4220    read_line_remove_files(All),
4221    ok.
4222%% read_line with file.
4223read_line_2(Config) when is_list(Config) ->
4224    PrivDir = proplists:get_value(priv_dir, Config),
4225    All = read_line_testdata(PrivDir),
4226    read_line_create_files(All),
4227    [ begin
4228	  io:format("read_line_all: ~s~n",[File]),
4229	  {X,_} = read_line_all2(File),
4230	  true
4231      end || {_,File,X,_} <- All ],
4232    [ begin
4233	  io:format("read_line_all_alternating: ~s~n",[File]),
4234	  {Y,_} = read_line_all_alternating2(File),
4235	  true
4236      end || {_,File,_,Y} <- All , Y =/= fail],
4237    [ begin
4238	  io:format("read_line_all_alternating (failing as should): ~s~n",[File]),
4239	  {'EXIT',_} = (catch read_line_all_alternating2(File)),
4240	  true
4241      end || {_,File,_,Y} <- All , Y =:= fail],
4242    read_line_remove_files(All),
4243    ok.
4244%% read_line with raw file.
4245read_line_3(Config) when is_list(Config) ->
4246    PrivDir = proplists:get_value(priv_dir, Config),
4247    All = read_line_testdata(PrivDir),
4248    read_line_create_files(All),
4249    [ begin
4250	  io:format("read_line_all: ~s~n",[File]),
4251	  {X,_} = read_line_all3(File),
4252	  true
4253      end || {_,File,X,_} <- All ],
4254    [ begin
4255	  io:format("read_line_all_alternating: ~s~n",[File]),
4256	  {Y,_} = read_line_all_alternating3(File),
4257	  true
4258      end || {_,File,_,Y} <- All , Y =/= fail],
4259    [ begin
4260	  io:format("read_line_all_alternating (failing as should): ~s~n",[File]),
4261	  {'EXIT',_} = (catch read_line_all_alternating3(File)),
4262	  true
4263      end || {_,File,_,Y} <- All , Y =:= fail],
4264    read_line_remove_files(All),
4265    ok.
4266%% read_line with raw buffered file.
4267read_line_4(Config) when is_list(Config) ->
4268    PrivDir = proplists:get_value(priv_dir, Config),
4269    All = read_line_testdata(PrivDir),
4270    read_line_create_files(All),
4271    [ begin
4272	  io:format("read_line_all: ~s~n",[File]),
4273	  {X,_} = read_line_all4(File),
4274	  true
4275      end || {_,File,X,_} <- All ],
4276    [ begin
4277	  io:format("read_line_all_alternating: ~s~n",[File]),
4278	  {Y,_} = read_line_all_alternating4(File),
4279	  true
4280      end || {_,File,_,Y} <- All , Y =/= fail],
4281    [ begin
4282	  io:format("read_line_all_alternating (failing as should): ~s~n",[File]),
4283	  {'EXIT',_} = (catch read_line_all_alternating4(File)),
4284	  true
4285      end || {_,File,_,Y} <- All , Y =:= fail],
4286    read_line_remove_files(All),
4287    ok.
4288
4289rl_lines() ->
4290    [ <<"hej">>,<<"hopp">>,<<"i">>,<<"lingon\rskogen">>].
4291
4292read_line_create0(Filename) ->
4293    {ok,F} = file:open(Filename,[write]),
4294    L = rl_lines(),
4295    [ file:write(F,[R,<<"\r\n">>]) || R <- L ],
4296    file:write(F,<<"Inget radslut\r">>),
4297    file:close(F).
4298read_line_create1(Filename) ->
4299    {ok,F} = file:open(Filename,[write]),
4300    L = rl_lines(),
4301    [ begin
4302	  [ file:write(F,[R,<<"\r\n">>]) || R <- L ],
4303	  file:write(F,<<"Inget radslut\r">>)
4304      end || _ <- lists:seq(1,100)],
4305    file:close(F).
4306read_line_create2(Filename) ->
4307    {ok,F} = file:open(Filename,[write]),
4308    L = rl_lines(),
4309    [ begin
4310	  [ file:write(F,[R]) || R <- L ],
4311	  file:write(F,<<"Inget radslut\r">>)
4312      end || _ <- lists:seq(1,200)],
4313    file:write(F,<<"\r\n">>),
4314    file:close(F).
4315
4316read_line_create3(Filename) ->
4317    {ok,F} = file:open(Filename,[write]),
4318    L = rl_lines(),
4319    [ begin
4320	  file:write(F,<<"\r\n">>),
4321	  file:write(F,<<"\r\n">>),
4322	  [ file:write(F,[R,<<"\r\n">>]) || R <- L ],
4323	  file:write(F,<<"Inget radslut\r">>)
4324      end || _ <- lists:seq(1,100)],
4325    file:close(F).
4326
4327read_line_create4(Filename) ->
4328    {ok,F} = file:open(Filename,[write]),
4329    L = rl_lines(),
4330    [ begin
4331	  file:write(F,<<"\n">>),
4332	  file:write(F,<<"\n">>),
4333	  [ file:write(F,[R,<<"\r\n">>]) || R <- L ],
4334	  file:write(F,<<"Inget radslut\r">>)
4335      end || _ <- lists:seq(1,100)],
4336    file:close(F).
4337
4338read_line_create5(Filename) ->
4339    {ok,F} = file:open(Filename,[write]),
4340    L = rl_lines(),
4341    [ begin
4342	  file:write(F,<<"i\n">>),
4343	  file:write(F,<<"i\n">>),
4344	  [ file:write(F,[R,<<"\r\n">>]) || R <- L ],
4345	  file:write(F,<<"Inget radslut\r">>)
4346      end || _ <- lists:seq(1,100)],
4347    file:close(F).
4348
4349read_line_create6(Filename) ->
4350    {ok,F} = file:open(Filename,[write]),
4351    L = rl_lines(),
4352    [ begin
4353	  file:write(F,<<"i\r\n">>),
4354	  file:write(F,<<"i\r\n">>),
4355	  [ file:write(F,[R,<<"\r\n">>]) || R <- L ],
4356	  file:write(F,<<"Inget radslut\r">>)
4357      end || _ <- lists:seq(1,100)],
4358    file:close(F).
4359read_line_create7(Filename) ->
4360    {ok,F} = file:open(Filename,[write]),
4361    L = rl_lines(),
4362    [ begin
4363	  [ file:write(F,[R,<<"\r\n">>]) || R <- L ],
4364	  file:write(F,<<"Inget radslut\r">>)
4365      end || _ <- lists:seq(1,1000)],
4366    file:close(F).
4367
4368read_line_all(Filename) ->
4369    {ok,F} = ?PRIM_FILE:open(Filename,[read,binary]),
4370    X=read_rl_lines(F),
4371    ?PRIM_FILE:close(F),
4372    Bin = list_to_binary([B || {ok,B} <- X]),
4373    Bin = re:replace(list_to_binary([element(2,file:read_file(Filename))]),
4374		     "\r\n","\n",[global,{return,binary}]),
4375    {length(X),Bin}.
4376
4377read_line_all2(Filename) ->
4378    {ok,F} = file:open(Filename,[read,binary]),
4379    X=read_rl_lines2(F),
4380    file:close(F),
4381    Bin = list_to_binary([B || {ok,B} <- X]),
4382    Bin = re:replace(list_to_binary([element(2,file:read_file(Filename))]),
4383		     "\r\n","\n",[global,{return,binary}]),
4384    {length(X),Bin}.
4385
4386read_line_all3(Filename) ->
4387    {ok,F} = file:open(Filename,[read,binary,raw]),
4388    X=read_rl_lines2(F),
4389    file:close(F),
4390    Bin = list_to_binary([B || {ok,B} <- X]),
4391    Bin = re:replace(list_to_binary([element(2,file:read_file(Filename))]),
4392		     "\r\n","\n",[global,{return,binary}]),
4393    {length(X),Bin}.
4394read_line_all4(Filename) ->
4395    {ok,F} = file:open(Filename,[read,binary,raw,{read_ahead,8192}]),
4396    X=read_rl_lines2(F),
4397    file:close(F),
4398    Bin = list_to_binary([B || {ok,B} <- X]),
4399    Bin = re:replace(list_to_binary([element(2,file:read_file(Filename))]),
4400		     "\r\n","\n",[global,{return,binary}]),
4401    {length(X),Bin}.
4402
4403read_rl_lines(F) ->
4404    case ?PRIM_FILE:read_line(F) of
4405	eof ->
4406	    [];
4407	{error,X} ->
4408	    {error,X};
4409	List ->
4410	    [List | read_rl_lines(F)]
4411    end.
4412
4413read_rl_lines2(F) ->
4414    case file:read_line(F) of
4415	eof ->
4416	    [];
4417	{error,X} ->
4418	    {error,X};
4419	List ->
4420	    [List | read_rl_lines2(F)]
4421    end.
4422
4423read_line_all_alternating(Filename) ->
4424    {ok,F} = ?PRIM_FILE:open(Filename,[read,binary]),
4425    X=read_rl_lines(F,true),
4426    ?PRIM_FILE:close(F),
4427    Bin = list_to_binary([B || {ok,B} <- X]),
4428    Bin = re:replace(list_to_binary([element(2,file:read_file(Filename))]),
4429		     "\r\n","\n",[global,{return,binary}]),
4430    {length(X),Bin}.
4431
4432read_line_all_alternating2(Filename) ->
4433    {ok,F} = file:open(Filename,[read,binary]),
4434    X=read_rl_lines2(F,true),
4435    file:close(F),
4436    Bin = list_to_binary([B || {ok,B} <- X]),
4437    Bin = re:replace(list_to_binary([element(2,file:read_file(Filename))]),
4438		     "\r\n","\n",[global,{return,binary}]),
4439    {length(X),Bin}.
4440read_line_all_alternating3(Filename) ->
4441    {ok,F} = file:open(Filename,[read,binary,raw]),
4442    X=read_rl_lines2(F,true),
4443    file:close(F),
4444    Bin = list_to_binary([B || {ok,B} <- X]),
4445    Bin = re:replace(list_to_binary([element(2,file:read_file(Filename))]),
4446		     "\r\n","\n",[global,{return,binary}]),
4447    {length(X),Bin}.
4448read_line_all_alternating4(Filename) ->
4449    {ok,F} = file:open(Filename,[read,binary,raw,{read_ahead,8192}]),
4450    X=read_rl_lines2(F,true),
4451    file:close(F),
4452    Bin = list_to_binary([B || {ok,B} <- X]),
4453    Bin = re:replace(list_to_binary([element(2,file:read_file(Filename))]),
4454		     "\r\n","\n",[global,{return,binary}]),
4455    {length(X),Bin}.
4456
4457read_rl_lines(F,Alternate) ->
4458    case begin
4459	     case Alternate of
4460		 true -> ?PRIM_FILE:read(F,1);
4461		 false -> ?PRIM_FILE:read_line(F)
4462	     end
4463	 end of
4464	eof ->
4465	    [];
4466	{error,X} ->
4467	    {error,X};
4468	List ->
4469	    [List | read_rl_lines(F,not Alternate)]
4470    end.
4471read_rl_lines2(F,Alternate) ->
4472    case begin
4473	     case Alternate of
4474		 true -> file:read(F,1);
4475		 false -> file:read_line(F)
4476	     end
4477	 end of
4478	eof ->
4479	    [];
4480	{error,X} ->
4481	    {error,X};
4482	List ->
4483	    [List | read_rl_lines2(F,not Alternate)]
4484    end.
4485
4486
4487%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4488
4489bytes(B, N)
4490  when is_integer(B), 0 =< B, B =< 255, is_integer(N), N > 2, N band 1 == 0 ->
4491    [bytes(B, N bsr 1), bytes(B, N bsr 1)];
4492bytes(B, 0)
4493  when is_integer(B), 0 =< B, B =< 255 ->
4494    [];
4495bytes(B, 2)
4496  when is_integer(B), 0 =< B, B =< 255 ->
4497    [B, B];
4498bytes(B, N)
4499  when is_integer(B), 0 =< B, B =< 255, is_integer(N), N > 0 ->
4500    [B, bytes(B, N-1)].
4501
4502
4503%% A simple loop construct.
4504%%
4505%% Calls 'Fun' with argument 'Start' first and then repeatedly with
4506%% its returned value (state) until 'Fun' returns 'Stop'. Then
4507%% the last state value that was not 'Stop' is returned.
4508
4509iterate(Start, Done, Fun) when is_function(Fun) ->
4510    iterate(Start, Done, Fun, Start).
4511
4512iterate(Done, Done, _Fun, I) ->
4513    I;
4514iterate(I, Done, Fun, _) ->
4515    iterate(Fun(I), Done, Fun, I).
4516
4517
4518
4519flush() ->
4520    flush([]).
4521
4522flush(Msgs) ->
4523    receive
4524	Msg ->
4525	    flush([Msg | Msgs])
4526    after 0 ->
4527	    lists:reverse(Msgs)
4528    end.
4529
4530%%%
4531%%% Support for testing large files.
4532%%%
4533
4534run_large_file_test(Config, Run, Name) ->
4535    case {os:type(),os:version()} of
4536	{{win32,nt},_} ->
4537	    do_run_large_file_test(Config, Run, Name);
4538	{{unix,sunos},OsVersion} when OsVersion < {5,5,1} ->
4539	    {skip,"Only supported on Win32, Unix or SunOS >= 5.5.1"};
4540	{{unix,_},_} ->
4541            case disc_free(proplists:get_value(priv_dir, Config)) of
4542                error ->
4543                    {skip, "Failed to query disk space for priv_dir. "
4544                           "Is it on a remote file system?~n"};
4545                N when N >= 5 * (1 bsl 20) ->
4546                    ct:pal("Free disk: ~w KByte~n", [N]),
4547                    do_run_large_file_test(Config, Run, Name);
4548                N when N < 5 * (1 bsl 20) ->
4549                    ct:pal("Free disk: ~w KByte~n", [N]),
4550                    {skip,"Less than 5 GByte free"}
4551            end;
4552	_ ->
4553	    {skip,"Only supported on Win32, Unix or SunOS >= 5.5.1"}
4554    end.
4555
4556
4557do_run_large_file_test(Config, Run, Name0) ->
4558    Name = filename:join(proplists:get_value(priv_dir, Config),
4559			 ?MODULE_STRING ++ Name0),
4560
4561    %% Set up a process that will delete this file.
4562    Tester = self(),
4563    Deleter =
4564	spawn(
4565	  fun() ->
4566		  Mref = erlang:monitor(process, Tester),
4567		  receive
4568		      {'DOWN',Mref,_,_,_} -> ok;
4569		      {Tester,done} -> ok
4570		  end,
4571		  ?FILE_MODULE:delete(Name)
4572	  end),
4573
4574    %% Run the test case.
4575    Res = Run(Name),
4576
4577    %% Delete file and finish deleter process.
4578    Mref = erlang:monitor(process, Deleter),
4579    Deleter ! {Tester,done},
4580    receive {'DOWN',Mref,_,_,_} -> ok end,
4581
4582    Res.
4583
4584disc_free(Path) ->
4585    Data = disksup:get_disk_data(),
4586
4587    %% What partitions could Data be mounted on?
4588    Partitions =
4589        [D || {P, _Tot, _Perc}=D <- Data,
4590         lists:prefix(filename:nativename(P), filename:nativename(Path))],
4591
4592    %% Sorting in descending order places the partition with the most specific
4593    %% path first.
4594    case lists:sort(fun erlang:'>='/2, Partitions) of
4595        [{_,Tot, Perc} | _] -> round(Tot * (1-(Perc/100)));
4596        [] -> error
4597    end.
4598
4599memsize() ->
4600    {Tot,_Used,_}  = memsup:get_memory_data(),
4601    Tot.
4602
4603%%%-----------------------------------------------------------------
4604%%% Utilities
4605rm_rf(Mod,Dir) ->
4606    case  Mod:read_link_info(Dir) of
4607	{ok, #file_info{type = directory}} ->
4608	    {ok, Content} = Mod:list_dir_all(Dir),
4609	    [ rm_rf(Mod,filename:join(Dir,C)) || C <- Content ],
4610	    Mod:del_dir(Dir),
4611	    ok;
4612	{ok, #file_info{}} ->
4613	    Mod:delete(Dir);
4614	_ ->
4615	    ok
4616    end.
4617