From 4c024f1d700cb8d75d13ed8ac6bf36ae98ce9ab6 Mon Sep 17 00:00:00 2001 From: Julius Putra Tanu Setiaji Date: Fri, 13 Sep 2019 16:12:16 +0800 Subject: Fix efile_allocate on Mac --- erts/emulator/nifs/unix/unix_prim_file.c | 42 +++++++++++++++++++++++++++++--- lib/kernel/test/file_SUITE.erl | 26 +++++++++++++++++++- 2 files changed, 64 insertions(+), 4 deletions(-) diff --git a/erts/emulator/nifs/unix/unix_prim_file.c b/erts/emulator/nifs/unix/unix_prim_file.c index 169b193993..fbf312a3b8 100644 --- a/erts/emulator/nifs/unix/unix_prim_file.c +++ b/erts/emulator/nifs/unix/unix_prim_file.c @@ -559,18 +559,54 @@ int efile_allocate(efile_data_t *d, Sint64 offset, Sint64 length) { } while(ret < 0 && errno == EINTR); #elif defined(F_PREALLOCATE) /* Mac-specific */ + off_t original_position, eof_offset; fstore_t fs = {}; + if(offset < 0 || length < 0 || (offset > ERTS_SINT64_MAX - length)) { + u->common.posix_errno = EINVAL; + return 0; + } + + original_position = lseek(u->fd, 0, SEEK_CUR); + + if(original_position < 0) { + u->common.posix_errno = errno; + return 0; + } + + eof_offset = lseek(u->fd, 0, SEEK_END); + + if(eof_offset < 0 || lseek(u->fd, original_position, SEEK_SET) < 0) { + u->common.posix_errno = errno; + return 0; + } + + if(offset + length <= eof_offset) { + /* File is already large enough. */ + return 1; + } + fs.fst_flags = F_ALLOCATECONTIG; - fs.fst_posmode = F_VOLPOSMODE; - fs.fst_offset = offset; - fs.fst_length = length; + fs.fst_posmode = F_PEOFPOSMODE; + fs.fst_offset = 0; + fs.fst_length = (offset + length) - eof_offset; ret = fcntl(u->fd, F_PREALLOCATE, &fs); if(ret < 0) { fs.fst_flags = F_ALLOCATEALL; ret = fcntl(u->fd, F_PREALLOCATE, &fs); } + + if(ret >= 0) { + /* We MUST truncate since F_PREALLOCATE works relative to end-of-file, + * otherwise we will expand the file on repeated calls to + * file:allocate/3 with the same arguments. */ + ret = ftruncate(u->fd, offset + length); + if(ret < 0) { + u->common.posix_errno = errno; + return 0; + } + } #elif !defined(HAVE_POSIX_FALLOCATE) u->common.posix_errno = ENOTSUP; return 0; diff --git a/lib/kernel/test/file_SUITE.erl b/lib/kernel/test/file_SUITE.erl index 3bc8e6e828..7686aeee7d 100644 --- a/lib/kernel/test/file_SUITE.erl +++ b/lib/kernel/test/file_SUITE.erl @@ -94,6 +94,8 @@ -export([allocate/1]). +-export([allocate_file_size/1]). + -export([standard_io/1,mini_server/1]). -export([old_io_protocol/1]). @@ -145,7 +147,7 @@ groups() -> {files, [], [{group, open}, {group, pos}, {group, file_info}, {group, consult}, {group, eval}, {group, script}, - truncate, sync, datasync, advise, allocate]}, + truncate, sync, datasync, advise, allocate, allocate_file_size]}, {open, [], [open1, old_modes, new_modes, path_open, close, access, read_write, pread_write, append, open_errors, @@ -2036,6 +2038,28 @@ allocate_and_assert(Fd, Offset, Length) -> _ = Result end. +%% Tests that asserts that file:allocate/3 changes file size +allocate_file_size(Config) when is_list(Config) -> + case os:type() of + {win32, _} -> + {skip, "Windows does not support file:allocate/3"}; + + {unix, linux} -> + {skip, "file:allocate/3 on Linux does not change file size"}; + + _ -> + PrivDir = proplists:get_value(priv_dir, Config), + Allocate = filename:join(PrivDir, atom_to_list(?MODULE)++"_allocate_file"), + + {ok, Fd} = ?FILE_MODULE:open(Allocate, [write]), + ok = ?FILE_MODULE:allocate(Fd, 0, 1024), + {ok, 1024} = ?FILE_MODULE:position(Fd, eof), + ok = ?FILE_MODULE:close(Fd), + + [] = flush(), + ok + end. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% delete(Config) when is_list(Config) -> -- cgit v1.2.1 From f8a09b16abe083f4686405b07318cb7b41a1cf44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Thu, 19 Sep 2019 09:45:07 +0200 Subject: file_SUITE: Only test allocate/3 size updates on known ok platforms Support is spotty in our test platform, especially on the rigs that run the tests on funny file systems like NFS. We therefore limit the test to the rigs where we know it will work under normal circumstances. That the call works at all is tested in allocate/1, this commit only skips the size assertion test. --- lib/kernel/test/file_SUITE.erl | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/kernel/test/file_SUITE.erl b/lib/kernel/test/file_SUITE.erl index 7686aeee7d..1c1b35abc1 100644 --- a/lib/kernel/test/file_SUITE.erl +++ b/lib/kernel/test/file_SUITE.erl @@ -2041,13 +2041,7 @@ allocate_and_assert(Fd, Offset, Length) -> %% Tests that asserts that file:allocate/3 changes file size allocate_file_size(Config) when is_list(Config) -> case os:type() of - {win32, _} -> - {skip, "Windows does not support file:allocate/3"}; - - {unix, linux} -> - {skip, "file:allocate/3 on Linux does not change file size"}; - - _ -> + {unix, darwin} -> PrivDir = proplists:get_value(priv_dir, Config), Allocate = filename:join(PrivDir, atom_to_list(?MODULE)++"_allocate_file"), @@ -2057,7 +2051,13 @@ allocate_file_size(Config) when is_list(Config) -> ok = ?FILE_MODULE:close(Fd), [] = flush(), - ok + ok; + {unix, linux} -> + {skip, "file:allocate/3 on Linux does not change file size"}; + {win32, _} -> + {skip, "Windows does not support file:allocate/3"}; + _ -> + {skip, "Support for allocate/3 is spotty in our test platform at the moment."} end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -- cgit v1.2.1