diff --git a/erts/emulator/nifs/common/prim_file_nif.c b/erts/emulator/nifs/common/prim_file_nif.c index 4acedf169245..a43a75719fa1 100644 --- a/erts/emulator/nifs/common/prim_file_nif.c +++ b/erts/emulator/nifs/common/prim_file_nif.c @@ -102,6 +102,9 @@ static ERL_NIF_TERM read_file_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM a static ERL_NIF_TERM open_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM close_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM copy_file_range_available_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM copy_file_range_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]); + static ERL_NIF_TERM file_desc_to_ref_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]); /* Internal ops */ @@ -185,6 +188,8 @@ static ErlNifFunc nif_funcs[] = { {"allocate_nif", 3, allocate_nif, ERL_NIF_DIRTY_JOB_IO_BOUND}, {"advise_nif", 4, advise_nif, ERL_NIF_DIRTY_JOB_IO_BOUND}, {"read_handle_info_nif", 1, read_handle_info_nif, ERL_NIF_DIRTY_JOB_IO_BOUND}, + {"copy_file_range_available_nif", 0, copy_file_range_available_nif, ERL_NIF_DIRTY_JOB_IO_BOUND}, + {"copy_file_range_nif", 3, copy_file_range_nif, ERL_NIF_DIRTY_JOB_IO_BOUND}, /* Filesystem ops */ {"make_hard_link_nif", 2, make_hard_link_nif, ERL_NIF_DIRTY_JOB_IO_BOUND}, @@ -1399,3 +1404,81 @@ static ERL_NIF_TERM altname_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM arg return enif_make_tuple2(env, am_ok, result); } + +static ERL_NIF_TERM copy_file_range_available_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { + off64_t ret; + + ret = syscall(SYS_copy_file_range, -1, NULL, -1, NULL, 0, 0); + + if (ret == -1 && errno == ENOSYS) { + return enif_make_atom(env, "false"); + } else { + return enif_make_atom(env, "true"); + } +} + +static ERL_NIF_TERM copy_file_range_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { + posix_errno_t posix_errno, ignored; + efile_data_t *d_in, *d_out; + off64_t ret, len; + efile_fileinfo_t info = {0}; + efile_path_t file_path_in, file_path_out; + char atom_name[256]; + + ASSERT(argc == 3); + + if ((posix_errno = efile_marshal_path(env, argv[0], &file_path_in))) { + return posix_error_to_tuple(env, posix_errno); + } + + if ((posix_errno = efile_marshal_path(env, argv[1], &file_path_out))) { + return posix_error_to_tuple(env, posix_errno); + } + + // Check if the third argument is an atom + if (enif_get_atom(env, argv[2], atom_name, sizeof(atom_name), ERL_NIF_LATIN1)) { + if (strcmp(atom_name, "infinity") == 0) { + if ((posix_errno = efile_open(&file_path_in, EFILE_MODE_READ, efile_resource_type, &d_in))) { + return posix_error_to_tuple(env, posix_errno); + } + + if ((posix_errno = efile_read_handle_info(d_in, &info))) { + return posix_error_to_tuple(env, posix_errno); + } + + len = (off64_t)info.size; + efile_close(d_in, &ignored); + } + } + // Check if the third argument is an integer + else if (!enif_get_int64(env, argv[2], &len)) { + return enif_make_tuple2(env, enif_make_atom(env, "error"), enif_make_atom(env, "badarg")); + } + + if (len < 0) { + return enif_make_badarg(env); + } + + if ((posix_errno = efile_open(&file_path_in, EFILE_MODE_READ, efile_resource_type, &d_in))) { + return posix_error_to_tuple(env, posix_errno); + } + + if ((posix_errno = efile_open(&file_path_out, EFILE_MODE_WRITE, efile_resource_type, &d_out))) { + efile_close(d_in, &ignored); + return posix_error_to_tuple(env, posix_errno); + } + + // Copy file contents using SYS_copy_file_range syscall + ret = efile_copy_file_range(d_in, d_out, len); + + // Close file descriptors + efile_close(d_in, &ignored); + efile_close(d_out, &ignored); + + // Handle the return value and errors + if (ret == -1) { + return enif_make_tuple2(env, enif_make_atom(env, "ok"), enif_make_atom(env, "syscall failed")) ; + } else { + return enif_make_tuple2(env, enif_make_atom(env, "ok"), enif_make_uint(env, (unsigned int)ret)); + } +} diff --git a/erts/emulator/nifs/common/prim_file_nif.h b/erts/emulator/nifs/common/prim_file_nif.h index a9cb74ab0b4b..48850c9f395f 100644 --- a/erts/emulator/nifs/common/prim_file_nif.h +++ b/erts/emulator/nifs/common/prim_file_nif.h @@ -169,6 +169,7 @@ int efile_sync(efile_data_t *d, int data_only); int efile_advise(efile_data_t *d, Sint64 offset, Sint64 length, enum efile_advise_t advise); int efile_allocate(efile_data_t *d, Sint64 offset, Sint64 length); int efile_truncate(efile_data_t *d); +int efile_copy_file_range(efile_data_t *d_in, efile_data_t *d_out, Sint64 length); posix_errno_t efile_open(const efile_path_t *path, enum efile_modes_t modes, ErlNifResourceType *nif_type, efile_data_t **d); diff --git a/erts/emulator/nifs/unix/unix_prim_file.c b/erts/emulator/nifs/unix/unix_prim_file.c index 2bb51651ef9e..ddae8f0c214d 100644 --- a/erts/emulator/nifs/unix/unix_prim_file.c +++ b/erts/emulator/nifs/unix/unix_prim_file.c @@ -1085,3 +1085,14 @@ posix_errno_t efile_altname(ErlNifEnv *env, const efile_path_t *path, ERL_NIF_TE return ENOTSUP; } + + +int efile_copy_file_range(efile_data_t *d_in, efile_data_t *d_out, Sint64 length) { + efile_unix_t *u_in = (efile_unix_t*)d_in; + efile_unix_t *u_out = (efile_unix_t*)d_out; + off64_t ret; + + ret = syscall(SYS_copy_file_range, u_in->fd, NULL, u_out->fd, NULL, length, 0); + + return ret; +} diff --git a/erts/preloaded/ebin/prim_file.beam b/erts/preloaded/ebin/prim_file.beam index 3b4d892eed1f..38659409296e 100644 Binary files a/erts/preloaded/ebin/prim_file.beam and b/erts/preloaded/ebin/prim_file.beam differ diff --git a/erts/preloaded/src/prim_file.erl b/erts/preloaded/src/prim_file.erl index 3e9f578b656b..ce5da38ebf82 100644 --- a/erts/preloaded/src/prim_file.erl +++ b/erts/preloaded/src/prim_file.erl @@ -24,7 +24,8 @@ -export([open/2, close/1, sync/1, datasync/1, truncate/1, advise/4, allocate/3, read_line/1, read/2, write/2, position/2, - pread/2, pread/3, pwrite/2, pwrite/3]). + pread/2, pread/3, pwrite/2, pwrite/3, + copy_file_range_available/0, copy_file_range/3]). %% OTP internal. @@ -73,7 +74,8 @@ del_dir_nif/1, get_device_cwd_nif/1, set_cwd_nif/1, get_cwd_nif/0, ipread_s32bu_p32bu_nif/3, read_file_nif/1, get_handle_nif/1, delayed_close_nif/1, altname_nif/1, - file_desc_to_ref_nif/1]). + file_desc_to_ref_nif/1, copy_file_range_available_nif/0, + copy_file_range_nif/3]). -type prim_file_name() :: string() | unicode:unicode_binary(). -type prim_file_name_error() :: 'error' | 'ignore' | 'warning'. @@ -126,6 +128,13 @@ copy(#file_descriptor{module = ?MODULE} = Source, %% XXX Should be moved down to the driver for optimization. file:copy_opened(Source, Dest, Length). +copy_file_range(Source, Destination, ByteCount) + when (is_integer(ByteCount) andalso ByteCount >= 0) orelse is_atom(ByteCount) -> + copy_file_range_nif(encode_path(Source), encode_path(Destination), ByteCount); + +copy_file_range(_Source, _Destination, _ByteCount) -> {error, badarg}. +copy_file_range_available() -> + copy_file_range_available_nif(). open(Name, Modes) -> %% The try/catch pattern seen here is used throughout the file to adhere to %% the public file interface, which has leaked through for ages because of @@ -529,6 +538,10 @@ delayed_close_nif(_FileRef) -> erlang:nif_error(undef). read_handle_info_nif(_FileRef) -> erlang:nif_error(undef). +copy_file_range_available_nif() -> + erlang:nif_error(undef). +copy_file_range_nif(_Name,_Dest,_Size) -> + erlang:nif_error(undef). %% %% Quality-of-life helpers diff --git a/lib/kernel/src/Makefile b/lib/kernel/src/Makefile index 8ac476867407..28ecde9093bd 100644 --- a/lib/kernel/src/Makefile +++ b/lib/kernel/src/Makefile @@ -151,7 +151,7 @@ MODULES = \ raw_file_io_deflate \ raw_file_io_delayed \ raw_file_io_list \ - wrap_log_reader + wrap_log_reader HRL_FILES= ../include/file.hrl ../include/inet.hrl ../include/inet_sctp.hrl \ ../include/dist.hrl ../include/dist_util.hrl \ diff --git a/lib/kernel/src/file.erl b/lib/kernel/src/file.erl index 21de84b6c55f..0a4014c57a15 100644 --- a/lib/kernel/src/file.erl +++ b/lib/kernel/src/file.erl @@ -907,7 +907,10 @@ copy_int(Source, {_DestName, DestOpts} = Dest, Length) %% Both must be bare filenames. If they are not, %% the filename check in the copy operation will yell. copy_int(Source, Dest, Length) -> - copy_int({Source, []}, {Dest, []}, Length). + case prim_file:copy_file_range_available() of + true -> prim_file:copy_file_range(Source, Dest, Length); + false -> copy_int({Source, []}, {Dest, []}, Length) + end. diff --git a/lib/kernel/src/kernel.app.src b/lib/kernel/src/kernel.app.src index e871fbb2cc1d..ec06a9f3be45 100644 --- a/lib/kernel/src/kernel.app.src +++ b/lib/kernel/src/kernel.app.src @@ -122,7 +122,8 @@ seq_trace, socket, standard_error, - wrap_log_reader]}, + wrap_log_reader, + file_nif]}, {registered, [application_controller, erl_reply, auth,