diff options
Diffstat (limited to 'lib/kernel')
| -rw-r--r-- | lib/kernel/doc/src/file.xml | 13 | ||||
| -rw-r--r-- | lib/kernel/src/code.erl | 2 | ||||
| -rw-r--r-- | lib/kernel/src/file.erl | 11 | ||||
| -rw-r--r-- | lib/kernel/src/inet.erl | 2 | ||||
| -rw-r--r-- | lib/kernel/src/net_kernel.erl | 16 | ||||
| -rw-r--r-- | lib/kernel/src/os.erl | 31 | ||||
| -rw-r--r-- | lib/kernel/test/file_SUITE.erl | 104 | ||||
| -rw-r--r-- | lib/kernel/test/os_SUITE.erl | 7 | ||||
| -rw-r--r-- | lib/kernel/test/prim_file_SUITE.erl | 20 |
9 files changed, 183 insertions, 23 deletions
diff --git a/lib/kernel/doc/src/file.xml b/lib/kernel/doc/src/file.xml index 382262d1ee..a9ceac0bcf 100644 --- a/lib/kernel/doc/src/file.xml +++ b/lib/kernel/doc/src/file.xml @@ -603,7 +603,7 @@ f.txt: {person, "kalle", 25}. <type> <v>Filename = name()</v> <v>Modes = [Mode]</v> - <v> Mode = read | write | append | raw | binary | {delayed_write, Size, Delay} | delayed_write | {read_ahead, Size} | read_ahead | compressed</v> + <v> Mode = read | write | append | exclusive | raw | binary | {delayed_write, Size, Delay} | delayed_write | {read_ahead, Size} | read_ahead | compressed</v> <v> Size = Delay = int()</v> <v>IoDevice = io_device()</v> <v>Reason = ext_posix() | system_limit</v> @@ -630,6 +630,17 @@ f.txt: {person, "kalle", 25}. file opened with <c>append</c> will take place at the end of the file.</p> </item> + <tag><c>exclusive</c></tag> + <item> + <p>The file, when opened for writing, is created if it + does not exist. If the file exists, open will return + <c>{error, eexist}</c>.</p> + <warning><p>This option does not guarantee exclusiveness on + file systems that do not support O_EXCL properly, + such as NFS. Do not depend on this option unless you + know that the file system supports it (in general, local + file systems should be safe).</p></warning> + </item> <tag><c>raw</c></tag> <item> <p>The <c>raw</c> option allows faster access to a file, diff --git a/lib/kernel/src/code.erl b/lib/kernel/src/code.erl index affa5fc0fd..42d4818f08 100644 --- a/lib/kernel/src/code.erl +++ b/lib/kernel/src/code.erl @@ -66,6 +66,8 @@ set_primary_archive/3, clash/0]). +-export_type([load_error_rsn/0, load_ret/0]). + -include_lib("kernel/include/file.hrl"). %% User interface. diff --git a/lib/kernel/src/file.erl b/lib/kernel/src/file.erl index a694ed0708..cfdd7045bd 100644 --- a/lib/kernel/src/file.erl +++ b/lib/kernel/src/file.erl @@ -61,6 +61,9 @@ -export([ipread_s32bu_p32bu_int/3]). +%% Types that can be used from other modules -- alphabetically ordered. +-export_type([date_time/0, fd/0, file_info/0, filename/0, io_device/0, + name/0, posix/0]). %%% Includes and defines -include("file.hrl"). @@ -81,7 +84,7 @@ -type mode() :: 'read' | 'write' | 'append' | 'raw' | 'binary' | {'delayed_write', non_neg_integer(), non_neg_integer()} | 'delayed_write' | {'read_ahead', pos_integer()} | - 'read_ahead' | 'compressed'. + 'read_ahead' | 'compressed' | 'exclusive'. -type name() :: string() | atom() | [name()]. -type posix() :: atom(). -type bindings() :: any(). @@ -369,7 +372,7 @@ advise(_, _, _, _) -> -spec read(File :: io_device(), Size :: non_neg_integer()) -> 'eof' | {'ok', [char()] | binary()} | {'error', posix()}. -read(File, Sz) when is_pid(File), is_integer(Sz), Sz >= 0 -> +read(File, Sz) when (is_pid(File) orelse is_atom(File)), is_integer(Sz), Sz >= 0 -> case io:request(File, {get_chars, '', Sz}) of Data when is_list(Data); is_binary(Data) -> {ok, Data}; @@ -385,7 +388,7 @@ read(_, _) -> -spec read_line(File :: io_device()) -> 'eof' | {'ok', [char()] | binary()} | {'error', posix()}. -read_line(File) when is_pid(File) -> +read_line(File) when (is_pid(File) orelse is_atom(File)) -> case io:request(File, {get_line, ''}) of Data when is_list(Data); is_binary(Data) -> {ok, Data}; @@ -439,7 +442,7 @@ pread(_, _, _) -> -spec write(File :: io_device(), Byte :: iodata()) -> 'ok' | {'error', posix()}. -write(File, Bytes) when is_pid(File) -> +write(File, Bytes) when (is_pid(File) orelse is_atom(File)) -> case make_binary(Bytes) of Bin when is_binary(Bin) -> io:request(File, {put_chars,Bin}); diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl index eb503235d8..93d75321ba 100644 --- a/lib/kernel/src/inet.erl +++ b/lib/kernel/src/inet.erl @@ -62,6 +62,8 @@ %% timer interface -export([start_timer/1, timeout/1, timeout/2, stop_timer/1]). +-export_type([ip_address/0, socket/0]). + %% imports -import(lists, [append/1, duplicate/2, filter/2, foldl/3]). diff --git a/lib/kernel/src/net_kernel.erl b/lib/kernel/src/net_kernel.erl index 0e17c059e5..0e5cc8c2c6 100644 --- a/lib/kernel/src/net_kernel.erl +++ b/lib/kernel/src/net_kernel.erl @@ -1086,11 +1086,11 @@ do_spawn(SpawnFuncArgs, SpawnOpts, State) -> spawn_func(link,{From,Tag},M,F,A,Gleader) -> link(From), - async_gen_server_reply({From,Tag},self()), %% ahhh + gen_server:reply({From,Tag},self()), %% ahhh group_leader(Gleader,self()), apply(M,F,A); spawn_func(_,{From,Tag},M,F,A,Gleader) -> - async_gen_server_reply({From,Tag},self()), %% ahhh + gen_server:reply({From,Tag},self()), %% ahhh group_leader(Gleader,self()), apply(M,F,A). @@ -1527,10 +1527,12 @@ async_gen_server_reply(From, Msg) -> {Pid, Tag} = From, M = {Tag, Msg}, case catch erlang:send(Pid, M, [nosuspend, noconnect]) of - true -> - M; - false -> - spawn(fun() -> gen_server:reply(From, Msg) end); - EXIT -> + ok -> + ok; + nosuspend -> + spawn(fun() -> catch erlang:send(Pid, M, [noconnect]) end); + noconnect -> + ok; % The gen module takes care of this case. + {'EXIT', _}=EXIT -> EXIT end. diff --git a/lib/kernel/src/os.erl b/lib/kernel/src/os.erl index d0b498edc9..75a11a8afd 100644 --- a/lib/kernel/src/os.erl +++ b/lib/kernel/src/os.erl @@ -50,7 +50,7 @@ find_executable(Name, Path) -> relative -> find_executable1(Name, split_path(Path), Extensions); _ -> - case verify_executable(Name, Extensions) of + case verify_executable(Name, Extensions, Extensions) of {ok, Complete} -> Complete; error -> @@ -60,7 +60,7 @@ find_executable(Name, Path) -> find_executable1(Name, [Base|Rest], Extensions) -> Complete0 = filename:join(Base, Name), - case verify_executable(Complete0, Extensions) of + case verify_executable(Complete0, Extensions, Extensions) of {ok, Complete} -> Complete; error -> @@ -69,7 +69,7 @@ find_executable1(Name, [Base|Rest], Extensions) -> find_executable1(_Name, [], _Extensions) -> false. -verify_executable(Name0, [Ext|Rest]) -> +verify_executable(Name0, [Ext|Rest], OrigExtensions) -> Name1 = Name0 ++ Ext, case os:type() of vxworks -> @@ -78,7 +78,7 @@ verify_executable(Name0, [Ext|Rest]) -> {ok, _} -> {ok, Name1}; _ -> - verify_executable(Name0, Rest) + verify_executable(Name0, Rest, OrigExtensions) end; _ -> case file:read_file_info(Name1) of @@ -87,12 +87,30 @@ verify_executable(Name0, [Ext|Rest]) -> %% on Unix, since we test if any execution bit is set. {ok, Name1}; _ -> - verify_executable(Name0, Rest) + verify_executable(Name0, Rest, OrigExtensions) end end; -verify_executable(_, []) -> +verify_executable(Name, [], OrigExtensions) when OrigExtensions =/= [""] -> %% Windows + %% Will only happen on windows, hence case insensitivity + case can_be_full_name(string:to_lower(Name),OrigExtensions) of + true -> + verify_executable(Name,[""],[""]); + _ -> + error + end; +verify_executable(_, [], _) -> error. +can_be_full_name(_Name,[]) -> + false; +can_be_full_name(Name,[H|T]) -> + case lists:suffix(H,Name) of %% Name is in lowercase, cause this is a windows thing + true -> + true; + _ -> + can_be_full_name(Name,T) + end. + split_path(Path) -> case type() of {win32, _} -> @@ -119,6 +137,7 @@ reverse_element(List) -> lists:reverse(List). -spec extensions() -> [string()]. +%% Extensions in lower case extensions() -> case type() of {win32, _} -> [".exe",".com",".cmd",".bat"]; diff --git a/lib/kernel/test/file_SUITE.erl b/lib/kernel/test/file_SUITE.erl index 1d652679b0..17c47f871d 100644 --- a/lib/kernel/test/file_SUITE.erl +++ b/lib/kernel/test/file_SUITE.erl @@ -53,7 +53,7 @@ -export([file_info/1, file_info_basic_file/1, file_info_basic_directory/1, file_info_bad/1, file_info_times/1, file_write_file_info/1]). -export([rename/1, access/1, truncate/1, datasync/1, sync/1, - read_write/1, pread_write/1, append/1]). + read_write/1, pread_write/1, append/1, exclusive/1]). -export([errors/1, e_delete/1, e_rename/1, e_make_dir/1, e_del_dir/1]). -export([otp_5814/1]). @@ -84,6 +84,8 @@ -export([advise/1]). +-export([standard_io/1,mini_server/1]). + %% Debug exports -export([create_file_slow/2, create_file/2, create_bin/2]). -export([verify_file/2, verify_bin/3]). @@ -103,7 +105,8 @@ all(suite) -> compression, links, copy, delayed_write, read_ahead, segment_read, segment_write, ipread, pid2name, interleaved_read_write, - otp_5814, large_file, read_line_1, read_line_2, read_line_3, read_line_4], + otp_5814, large_file, read_line_1, read_line_2, read_line_3, read_line_4, + standard_io], fini}. init(Config) when is_list(Config) -> @@ -172,6 +175,85 @@ time_dist({_D1, _T1} = DT1, {_D2, _T2} = DT2) -> - calendar:datetime_to_gregorian_seconds(DT1). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +mini_server(Parent) -> + receive + die -> + ok; + {io_request,From,To,{put_chars,Data}} -> + Parent ! {io_request,From,To,{put_chars,Data}}, + From ! {io_reply, To, ok}, + mini_server(Parent); + {io_request,From,To,{get_chars,'',N}} -> + Parent ! {io_request,From,To,{get_chars,'',N}}, + From ! {io_reply, To, {ok, lists:duplicate(N,$a)}}, + mini_server(Parent); + {io_request,From,To,{get_line,''}} -> + Parent ! {io_request,From,To,{get_line,''}}, + From ! {io_reply, To, {ok, "hej\n"}}, + mini_server(Parent) + end. + +standard_io(suite) -> + []; +standard_io(doc) -> + ["Test that standard i/o-servers work with file module"]; +standard_io(Config) when is_list(Config) -> + %% Really just a smoke test + ?line Pid = spawn(?MODULE,mini_server,[self()]), + ?line register(mini_server,Pid), + ?line ok = file:write(mini_server,<<"hej\n">>), + ?line receive + {io_request,_,_,{put_chars,<<"hej\n">>}} -> + ok + after 1000 -> + exit(noreply) + end, + ?line {ok,"aaaaa"} = file:read(mini_server,5), + ?line receive + {io_request,_,_,{get_chars,'',5}} -> + ok + after 1000 -> + exit(noreply) + end, + ?line {ok,"hej\n"} = file:read_line(mini_server), + ?line receive + {io_request,_,_,{get_line,''}} -> + ok + after 1000 -> + exit(noreply) + end, + ?line OldGL = group_leader(), + ?line group_leader(Pid,self()), + ?line ok = file:write(standard_io,<<"hej\n">>), + ?line group_leader(OldGL,self()), + ?line receive + {io_request,_,_,{put_chars,<<"hej\n">>}} -> + ok + after 1000 -> + exit(noreply) + end, + ?line group_leader(Pid,self()), + ?line {ok,"aaaaa"} = file:read(standard_io,5), + ?line group_leader(OldGL,self()), + ?line receive + {io_request,_,_,{get_chars,'',5}} -> + ok + after 1000 -> + exit(noreply) + end, + ?line group_leader(Pid,self()), + ?line {ok,"hej\n"} = file:read_line(standard_io), + ?line group_leader(OldGL,self()), + ?line receive + {io_request,_,_,{get_line,''}} -> + ok + after 1000 -> + exit(noreply) + end, + Pid ! die, + receive after 1000 -> ok end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% read_write_file(suite) -> []; read_write_file(doc) -> []; @@ -384,7 +466,7 @@ files(suite) -> sync,datasync,advise]. open(suite) -> [open1,old_modes,new_modes,path_open,close,access,read_write, - pread_write,append,open_errors]. + pread_write,append,open_errors,exclusive]. open1(suite) -> []; open1(doc) -> []; @@ -758,6 +840,22 @@ open_errors(Config) when is_list(Config) -> ?line test_server:timetrap_cancel(Dog), ok. +exclusive(suite) -> []; +exclusive(doc) -> "Test exclusive access to a file."; +exclusive(Config) when is_list(Config) -> + ?line Dog = test_server:timetrap(test_server:seconds(5)), + ?line RootDir = ?config(priv_dir,Config), + ?line NewDir = filename:join(RootDir, + atom_to_list(?MODULE) + ++"_exclusive"), + ?line ok = ?FILE_MODULE:make_dir(NewDir), + ?line Name = filename:join(NewDir, "ex_file.txt"), + ?line {ok, Fd} = ?FILE_MODULE:open(Name, [write, exclusive]), + ?line {error, eexist} = ?FILE_MODULE:open(Name, [write, exclusive]), + ?line ok = ?FILE_MODULE:close(Fd), + ?line test_server:timetrap_cancel(Dog), + ok. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% pos(suite) -> [pos1,pos2]. diff --git a/lib/kernel/test/os_SUITE.erl b/lib/kernel/test/os_SUITE.erl index 6a3534b094..ace9501d18 100644 --- a/lib/kernel/test/os_SUITE.erl +++ b/lib/kernel/test/os_SUITE.erl @@ -137,6 +137,13 @@ find_executable(Config) when is_list(Config) -> ?line find_exe(Abin, "my_ar", ".exe", Path), ?line find_exe(Abin, "my_ascii", ".com", Path), ?line find_exe(Abin, "my_adb", ".bat", Path), + %% OTP-3626 find names of executables given with extension + ?line find_exe(Abin, "my_ar.exe", "", Path), + ?line find_exe(Abin, "my_ascii.com", "", Path), + ?line find_exe(Abin, "my_adb.bat", "", Path), + ?line find_exe(Abin, "my_ar.EXE", "", Path), + ?line find_exe(Abin, "my_ascii.COM", "", Path), + ?line find_exe(Abin, "MY_ADB.BAT", "", Path), %% Search for programs in Abin (second element in PATH). ?line find_exe(Abin, "my_ar", ".exe", Path), diff --git a/lib/kernel/test/prim_file_SUITE.erl b/lib/kernel/test/prim_file_SUITE.erl index 21bdc06fdc..1688ec45ca 100644 --- a/lib/kernel/test/prim_file_SUITE.erl +++ b/lib/kernel/test/prim_file_SUITE.erl @@ -35,7 +35,7 @@ file_write_file_info_a/1, file_write_file_info_b/1]). -export([rename_a/1, rename_b/1, access/1, truncate/1, datasync/1, sync/1, - read_write/1, pread_write/1, append/1]). + read_write/1, pread_write/1, append/1, exclusive/1]). -export([errors/1, e_delete/1, e_rename/1, e_make_dir/1, e_del_dir/1]). -export([compression/1, read_not_really_compressed/1, @@ -385,7 +385,7 @@ win_cur_dir_1(_Config, Handle) -> files(suite) -> [open,pos,file_info,truncate,sync,datasync,advise]. open(suite) -> [open1,modes,close,access,read_write, - pread_write,append]. + pread_write,append,exclusive]. open1(suite) -> []; open1(doc) -> []; @@ -610,6 +610,22 @@ append(Config) when is_list(Config) -> ?line test_server:timetrap_cancel(Dog), ok. +exclusive(suite) -> []; +exclusive(doc) -> "Test exclusive access to a file."; +exclusive(Config) when is_list(Config) -> + ?line Dog = test_server:timetrap(test_server:seconds(5)), + ?line RootDir = ?config(priv_dir,Config), + ?line NewDir = filename:join(RootDir, + atom_to_list(?MODULE) + ++"_exclusive"), + ?line ok = ?PRIM_FILE:make_dir(NewDir), + ?line Name = filename:join(NewDir, "ex_file.txt"), + ?line {ok,Fd} = ?PRIM_FILE:open(Name, [write, exclusive]), + ?line {error, eexist} = ?PRIM_FILE:open(Name, [write, exclusive]), + ?line ok = ?PRIM_FILE:close(Fd), + ?line test_server:timetrap_cancel(Dog), + ok. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% pos(suite) -> [pos1,pos2]. |
