diff options
Diffstat (limited to 'erts/emulator/nifs/unix/unix_prim_file.c')
-rw-r--r-- | erts/emulator/nifs/unix/unix_prim_file.c | 42 |
1 files changed, 39 insertions, 3 deletions
diff --git a/erts/emulator/nifs/unix/unix_prim_file.c b/erts/emulator/nifs/unix/unix_prim_file.c index 176a9318b2..e5099dd921 100644 --- a/erts/emulator/nifs/unix/unix_prim_file.c +++ b/erts/emulator/nifs/unix/unix_prim_file.c @@ -566,18 +566,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; |