diff options
author | Michael Santos <michael.santos@gmail.com> | 2010-04-30 12:22:11 -0400 |
---|---|---|
committer | Raimo Niskanen <raimo@erlang.org> | 2010-06-04 10:47:56 +0200 |
commit | 4bd026cccb2c22279dd9c88e704642c5a65d3c53 (patch) | |
tree | 5c801bf9813b575eb302039f78b07b50364b6696 | |
parent | cbd1378ee1fde835e55614bac9290b281bafe49a (diff) | |
download | otp-4bd026cccb2c22279dd9c88e704642c5a65d3c53.tar.gz otp-4bd026cccb2c22279dd9c88e704642c5a65d3c53.tar.bz2 otp-4bd026cccb2c22279dd9c88e704642c5a65d3c53.zip |
Support opening files in exclusive mode
Add an option that atomically tests for the existence of a file and
creates it if the file does not exist, by passing the O_EXCL flag
to open() on Unix and CREATE_NEW flag on Windows. Support for O_EXCL
varies across platforms and filesystems.
{ok, Fd} = file:open("/tmp/foo", [write,exclusive]),
{error, eexist} = file:open("/tmp/foo", [write,exclusive]).
-rw-r--r-- | erts/emulator/drivers/common/erl_efile.h | 3 | ||||
-rw-r--r-- | erts/emulator/drivers/unix/unix_efile.c | 3 | ||||
-rw-r--r-- | erts/emulator/drivers/win32/win_efile.c | 3 | ||||
-rw-r--r-- | erts/preloaded/src/prim_file.erl | 5 | ||||
-rw-r--r-- | lib/kernel/doc/src/file.xml | 13 | ||||
-rw-r--r-- | lib/kernel/src/file.erl | 2 | ||||
-rw-r--r-- | lib/kernel/test/file_SUITE.erl | 20 | ||||
-rw-r--r-- | lib/kernel/test/prim_file_SUITE.erl | 20 |
8 files changed, 61 insertions, 8 deletions
diff --git a/erts/emulator/drivers/common/erl_efile.h b/erts/emulator/drivers/common/erl_efile.h index bbc973d58b..ac95c1f949 100644 --- a/erts/emulator/drivers/common/erl_efile.h +++ b/erts/emulator/drivers/common/erl_efile.h @@ -32,7 +32,8 @@ #define EFILE_MODE_READ_WRITE 3 #define EFILE_MODE_APPEND 4 #define EFILE_COMPRESSED 8 -#define EFILE_NO_TRUNCATE 16 /* Special for reopening on VxWorks */ +#define EFILE_MODE_EXCL 16 +#define EFILE_NO_TRUNCATE 32 /* Special for reopening on VxWorks */ /* * Seek modes for efile_seek(). diff --git a/erts/emulator/drivers/unix/unix_efile.c b/erts/emulator/drivers/unix/unix_efile.c index ea016526ef..0052ac0739 100644 --- a/erts/emulator/drivers/unix/unix_efile.c +++ b/erts/emulator/drivers/unix/unix_efile.c @@ -706,6 +706,9 @@ efile_openfile(Efile_error* errInfo, /* Where to return error codes. */ #endif } + if (flags & EFILE_MODE_EXCL) { + mode |= O_EXCL; + } #ifdef VXWORKS if (*name != '/') { diff --git a/erts/emulator/drivers/win32/win_efile.c b/erts/emulator/drivers/win32/win_efile.c index 24b6fb30dc..04bd1139f5 100644 --- a/erts/emulator/drivers/win32/win_efile.c +++ b/erts/emulator/drivers/win32/win_efile.c @@ -689,6 +689,9 @@ Sint64* pSize; /* Where to store the size of the file. */ if (flags & EFILE_MODE_APPEND) { crFlags = OPEN_ALWAYS; } + if (flags & EFILE_MODE_EXCL) { + crFlags = CREATE_NEW; + } fd = CreateFile(name, access, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, crFlags, FILE_ATTRIBUTE_NORMAL, NULL); diff --git a/erts/preloaded/src/prim_file.erl b/erts/preloaded/src/prim_file.erl index 2d177bf80e..7f24889bb2 100644 --- a/erts/preloaded/src/prim_file.erl +++ b/erts/preloaded/src/prim_file.erl @@ -116,9 +116,10 @@ -define(EFILE_MODE_READ_WRITE, 3). -define(EFILE_MODE_APPEND, 4). -define(EFILE_COMPRESSED, 8). +-define(EFILE_MODE_EXCL, 16). %% Use this mask to get just the mode bits to be passed to the driver. --define(EFILE_MODE_MASK, 15). +-define(EFILE_MODE_MASK, 31). %% Seek modes for the driver's seek function. -define(EFILE_SEEK_SET, 0). @@ -958,6 +959,8 @@ open_mode([compressed|Rest], Mode, Portopts, Setopts) -> open_mode([append|Rest], Mode, Portopts, Setopts) -> open_mode(Rest, Mode bor ?EFILE_MODE_APPEND bor ?EFILE_MODE_WRITE, Portopts, Setopts); +open_mode([exclusive|Rest], Mode, Portopts, Setopts) -> + open_mode(Rest, Mode bor ?EFILE_MODE_EXCL, Portopts, Setopts); open_mode([delayed_write|Rest], Mode, Portopts, Setopts) -> open_mode([{delayed_write, 64*1024, 2000}|Rest], Mode, Portopts, Setopts); 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/file.erl b/lib/kernel/src/file.erl index a37614e424..1ba329a12b 100644 --- a/lib/kernel/src/file.erl +++ b/lib/kernel/src/file.erl @@ -81,7 +81,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(). diff --git a/lib/kernel/test/file_SUITE.erl b/lib/kernel/test/file_SUITE.erl index 15bab0dccd..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]). @@ -466,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) -> []; @@ -840,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/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]. |