Skip to content

Commit f959551

Browse files
committed
file: copy_file_range
1 parent 3348e03 commit f959551

File tree

6 files changed

+119
-3
lines changed

6 files changed

+119
-3
lines changed

erts/emulator/nifs/common/prim_file_nif.c

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,9 @@ static ERL_NIF_TERM read_file_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM a
102102
static ERL_NIF_TERM open_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
103103
static ERL_NIF_TERM close_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
104104

105+
static ERL_NIF_TERM copy_file_available_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
106+
static ERL_NIF_TERM copy_file_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
107+
105108
static ERL_NIF_TERM file_desc_to_ref_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
106109

107110
/* Internal ops */
@@ -185,6 +188,8 @@ static ErlNifFunc nif_funcs[] = {
185188
{"allocate_nif", 3, allocate_nif, ERL_NIF_DIRTY_JOB_IO_BOUND},
186189
{"advise_nif", 4, advise_nif, ERL_NIF_DIRTY_JOB_IO_BOUND},
187190
{"read_handle_info_nif", 1, read_handle_info_nif, ERL_NIF_DIRTY_JOB_IO_BOUND},
191+
{"copy_file_available_nif", 0, copy_file_available_nif, ERL_NIF_DIRTY_JOB_IO_BOUND},
192+
{"copy_file_nif", 3, copy_file_nif, ERL_NIF_DIRTY_JOB_IO_BOUND},
188193

189194
/* Filesystem ops */
190195
{"make_hard_link_nif", 2, make_hard_link_nif, ERL_NIF_DIRTY_JOB_IO_BOUND},
@@ -1399,3 +1404,76 @@ static ERL_NIF_TERM altname_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM arg
13991404

14001405
return enif_make_tuple2(env, am_ok, result);
14011406
}
1407+
1408+
static ERL_NIF_TERM copy_file_available_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
1409+
off64_t ret;
1410+
1411+
ret = syscall(SYS_copy_file_range, -1, NULL, -1, NULL, 0, 0);
1412+
1413+
if (ret == -1 && errno == ENOSYS) {
1414+
return enif_make_atom(env, "false");
1415+
} else {
1416+
return enif_make_atom(env, "true");
1417+
}
1418+
}
1419+
1420+
static ERL_NIF_TERM copy_file_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
1421+
posix_errno_t posix_errno, ignored;
1422+
efile_data_t *d_in, *d_out;
1423+
off64_t ret = 0, len;
1424+
efile_fileinfo_t info = {0};
1425+
efile_path_t file_path_in, file_path_out;
1426+
char atom_name[256];
1427+
1428+
ASSERT(argc == 2);
1429+
1430+
if ((posix_errno = efile_marshal_path(env, argv[0], &file_path_in))) {
1431+
return posix_error_to_tuple(env, posix_errno);
1432+
}
1433+
1434+
if ((posix_errno = efile_marshal_path(env, argv[1], &file_path_out))) {
1435+
return posix_error_to_tuple(env, posix_errno);
1436+
}
1437+
1438+
/* We need to open Source file in order to get file size*/
1439+
if (posix_errno = efile_open(&file_path_in, EFILE_MODE_READ, efile_resource_type, &d_in)) {
1440+
return posix_error_to_tuple(env, posix_errno);
1441+
}
1442+
1443+
if ((posix_errno = efile_read_handle_info(d_in, &info))) {
1444+
/* In error case we should close Source file */
1445+
erts_atomic32_set_acqb(&d_in->state, EFILE_STATE_CLOSED);
1446+
efile_close(d_in, &ignored);
1447+
return posix_error_to_tuple(env, posix_errno);
1448+
}
1449+
1450+
len = (off64_t)info.size;
1451+
1452+
if (len < 0) {
1453+
return enif_make_badarg(env);
1454+
}
1455+
1456+
if ((posix_errno = efile_open(&file_path_out, EFILE_MODE_WRITE, efile_resource_type, &d_out))) {
1457+
erts_atomic32_set_acqb(&d_in->state, EFILE_STATE_CLOSED);
1458+
efile_close(d_in, &ignored);
1459+
return posix_error_to_tuple(env, posix_errno);
1460+
}
1461+
1462+
/* Copy file contents using SYS_copy_file_range syscall */
1463+
if ((posix_errno = efile_copy_file_range(d_in, d_out, len, &ret))) {
1464+
erts_atomic32_set_acqb(&d_in->state, EFILE_STATE_CLOSED);
1465+
efile_close(d_in, &ignored);
1466+
erts_atomic32_set_acqb(&d_out->state, EFILE_STATE_CLOSED);
1467+
efile_close(d_out, &ignored);
1468+
return posix_error_to_tuple(env, posix_errno);
1469+
}
1470+
1471+
/* Close file descriptors
1472+
* Closing without moving the file into CLOSED state will crash the debug emulator.*/
1473+
erts_atomic32_set_acqb(&d_in->state, EFILE_STATE_CLOSED);
1474+
efile_close(d_in, &ignored);
1475+
erts_atomic32_set_acqb(&d_out->state, EFILE_STATE_CLOSED);
1476+
efile_close(d_out, &ignored);
1477+
1478+
return enif_make_tuple2(env, enif_make_atom(env, "ok"), enif_make_uint(env, (unsigned int)ret));
1479+
}

erts/emulator/nifs/common/prim_file_nif.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,8 @@ posix_errno_t efile_from_fd(int fd,
177177
ErlNifResourceType *nif_type,
178178
efile_data_t **d);
179179

180+
posix_errno_t efile_copy_file_range(efile_data_t *d_in, efile_data_t *d_out, off64_t length, off64_t *result);
181+
180182
/** @brief Closes a file. The file must have entered the CLOSED state prior to
181183
* calling this to prevent double close.
182184
*

erts/emulator/nifs/unix/unix_prim_file.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1085,3 +1085,26 @@ posix_errno_t efile_altname(ErlNifEnv *env, const efile_path_t *path, ERL_NIF_TE
10851085

10861086
return ENOTSUP;
10871087
}
1088+
1089+
1090+
posix_errno_t efile_copy_file_range(efile_data_t *d_in, efile_data_t *d_out, off64_t length, off64_t *result) {
1091+
efile_unix_t *u_in = (efile_unix_t*)d_in;
1092+
efile_unix_t *u_out = (efile_unix_t*)d_out;
1093+
off64_t ret;
1094+
1095+
ASSERT(u_in->fd != -1);
1096+
ASSERT(u_out->fd != -1);
1097+
1098+
/* Perform the unix copy file range syscall */
1099+
ret = syscall(SYS_copy_file_range, u_in->fd, NULL, u_out->fd, NULL, length, 0);
1100+
1101+
if (ret < 0) {
1102+
return errno;
1103+
}
1104+
1105+
if (result != NULL) {
1106+
(*result) = ret;
1107+
}
1108+
1109+
return 0;
1110+
}

erts/preloaded/ebin/prim_file.beam

-17.4 KB
Binary file not shown.

erts/preloaded/src/prim_file.erl

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@
2424
-export([open/2, close/1,
2525
sync/1, datasync/1, truncate/1, advise/4, allocate/3,
2626
read_line/1, read/2, write/2, position/2,
27-
pread/2, pread/3, pwrite/2, pwrite/3]).
27+
pread/2, pread/3, pwrite/2, pwrite/3,
28+
copy_file_available/0, copy_file/2]).
2829

2930
%% OTP internal.
3031

@@ -73,7 +74,8 @@
7374
del_dir_nif/1, get_device_cwd_nif/1, set_cwd_nif/1, get_cwd_nif/0,
7475
ipread_s32bu_p32bu_nif/3, read_file_nif/1,
7576
get_handle_nif/1, delayed_close_nif/1, altname_nif/1,
76-
file_desc_to_ref_nif/1]).
77+
file_desc_to_ref_nif/1, copy_file_available_nif/0,
78+
copy_file_nif/2]).
7779

7880
-type prim_file_name() :: string() | unicode:unicode_binary().
7981
-type prim_file_name_error() :: 'error' | 'ignore' | 'warning'.
@@ -126,6 +128,13 @@ copy(#file_descriptor{module = ?MODULE} = Source,
126128
%% XXX Should be moved down to the driver for optimization.
127129
file:copy_opened(Source, Dest, Length).
128130

131+
%% Returns {error, Reason} | {ok, BytesCopied}
132+
%% Using unix syscall copy_file_range(2)
133+
copy_file(Source, Destination) ->
134+
copy_file_nif(encode_path(Source), encode_path(Destination)).
135+
copy_file_available() ->
136+
copy_file_available_nif().
137+
129138
open(Name, Modes) ->
130139
%% The try/catch pattern seen here is used throughout the file to adhere to
131140
%% the public file interface, which has leaked through for ages because of
@@ -529,6 +538,10 @@ delayed_close_nif(_FileRef) ->
529538
erlang:nif_error(undef).
530539
read_handle_info_nif(_FileRef) ->
531540
erlang:nif_error(undef).
541+
copy_file_available_nif() ->
542+
erlang:nif_error(undef).
543+
copy_file_nif(_Name,_Dest) ->
544+
erlang:nif_error(undef).
532545

533546
%%
534547
%% Quality-of-life helpers

lib/kernel/src/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ MODULES = \
151151
raw_file_io_deflate \
152152
raw_file_io_delayed \
153153
raw_file_io_list \
154-
wrap_log_reader
154+
wrap_log_reader
155155

156156
HRL_FILES= ../include/file.hrl ../include/inet.hrl ../include/inet_sctp.hrl \
157157
../include/dist.hrl ../include/dist_util.hrl \

0 commit comments

Comments
 (0)