summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Högberg <john@erlang.org>2019-09-19 10:03:05 +0200
committerJohn Högberg <john@erlang.org>2019-09-19 10:03:05 +0200
commit7666df1840a389dc453983a04995b4b934172190 (patch)
tree00c8c0f8e46b575a3f139149a18f813f5b5acabc
parent9925f6e99e06b62eb988c3f2ecb42fcc0964ead3 (diff)
parentf8a09b16abe083f4686405b07318cb7b41a1cf44 (diff)
downloaderlang-7666df1840a389dc453983a04995b4b934172190.tar.gz
Merge branch 'john/merge-pr-2386' into maint
OTP-16074 * john/merge-pr-2386: file_SUITE: Only test allocate/3 size updates on known ok platforms Fix efile_allocate on Mac
-rw-r--r--erts/emulator/nifs/unix/unix_prim_file.c42
-rw-r--r--lib/kernel/test/file_SUITE.erl26
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..1c1b35abc1 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
+ {unix, darwin} ->
+ 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;
+ {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.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
delete(Config) when is_list(Config) ->