Skip to content

Commit 6021d44

Browse files
committed
file: copy_file_range
1 parent 462c696 commit 6021d44

File tree

7 files changed

+359
-5
lines changed

7 files changed

+359
-5
lines changed

lib/kernel/Makefile

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk
2626
include vsn.mk
2727
VSN = $(KERNEL_VSN)
2828

29-
SUB_DIRECTORIES = src doc/src examples
29+
SUB_DIRECTORIES = src c_src doc/src examples
30+
static_lib: SUB_DIRECTORIES = c_src
3031

3132
SPECIAL_TARGETS =
3233

@@ -38,4 +39,4 @@ include $(ERL_TOP)/make/otp_subdir.mk
3839
DIA_PLT_APPS=crypto compiler
3940
TEST_NEEDS_RELEASE=true
4041

41-
include $(ERL_TOP)/make/app_targets.mk
42+
include $(ERL_TOP)/make/app_targets.mk

lib/kernel/c_src/Makefile

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
#
2+
# %CopyrightBegin%
3+
#
4+
# Copyright Ericsson AB 2002-2023. All Rights Reserved.
5+
#
6+
# Licensed under the Apache License, Version 2.0 (the "License");
7+
# you may not use this file except in compliance with the License.
8+
# You may obtain a copy of the License at
9+
#
10+
# http://www.apache.org/licenses/LICENSE-2.0
11+
#
12+
# Unless required by applicable law or agreed to in writing, software
13+
# distributed under the License is distributed on an "AS IS" BASIS,
14+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
# See the License for the specific language governing permissions and
16+
# limitations under the License.
17+
#
18+
# %CopyrightEnd%
19+
#
20+
#
21+
include $(ERL_TOP)/make/target.mk
22+
include $(ERL_TOP)/make/$(TARGET)/otp.mk
23+
include $(ERL_TOP)/make/$(TARGET)/otp_ded.mk
24+
25+
CC = $(DED_CC)
26+
LD = $(DED_LD)
27+
LIBS = $(DED_LIBS)
28+
29+
# ----------------------------------------------------
30+
# Application version
31+
# ----------------------------------------------------
32+
include ../vsn.mk
33+
VSN=$(KERNEL_VSN)
34+
35+
# ----------------------------------------------------
36+
# Release directory specification
37+
# ----------------------------------------------------
38+
RELSYSDIR = $(RELEASE_PATH)/lib/kernel-$(VSN)
39+
40+
41+
# ----------------------------------------------------
42+
# FLAGS misc
43+
# ----------------------------------------------------
44+
ifeq ($(TYPE),debug)
45+
TYPEMARKER = .debug
46+
else
47+
TYPEMARKER =
48+
endif
49+
50+
# ----------------------------------------------------
51+
# FLAGS
52+
# ----------------------------------------------------
53+
CFLAGS = $(DED_INCLUDES) $(EI_INCLUDES) $(DED_CFLAGS)
54+
STATIC_CFLAGS = $(DED_INCLUDES) $(EI_INCLUDES) $(DED_STATIC_CFLAGS)
55+
LDFLAGS += $(DED_LDFLAGS)
56+
57+
# ----------------------------------------------------
58+
# Target Specs
59+
# ----------------------------------------------------
60+
61+
NIF_OBJ_FILES = $(OBJDIR)/file_nif.o
62+
NIF_STATIC_OBJ_FILES = $(OBJDIR)/file_nif_static.o
63+
64+
# Module and shared lib have to have same name of
65+
# static nifs to work
66+
NIF_SHARED_OBJ_FILE = $(LIBDIR)/file_nif.so
67+
NIF_LIB_FILE = $(LIBDIR)/file_nif.a
68+
CLIB_FLAGS =
69+
LN= ln -s
70+
71+
ifeq ($(USING_VC),yes)
72+
AR_OUT=-out:
73+
AR_FLAGS=
74+
else
75+
AR_OUT=
76+
ifeq ($(V),0)
77+
AR_FLAGS=rc
78+
else
79+
AR_FLAGS=rcv
80+
endif
81+
endif
82+
83+
ifndef RANLIB
84+
RANLIB=true
85+
endif
86+
87+
# ----------------------------------------------------
88+
# Targets
89+
# ----------------------------------------------------
90+
91+
_create_dirs := $(shell mkdir -p $(OBJDIR) $(LIBDIR))
92+
93+
opt: $(NIF_SHARED_OBJ_FILE)
94+
95+
$(filter-out opt, $(TYPES)):
96+
@${MAKE} TYPE=$@ opt
97+
98+
static_lib: $(NIF_LIB_FILE)
99+
100+
clean:
101+
rm -f core *~
102+
rm -f $(LIBDIR)/*
103+
rm -f $(OBJDIR)/*
104+
105+
docs:
106+
107+
# ----------------------------------------------------
108+
# Special Build Targets
109+
# ----------------------------------------------------
110+
111+
112+
$(OBJDIR)/%.o: %.c
113+
$(V_CC) -c $(CFLAGS) -O3 -o $@ $<
114+
115+
$(OBJDIR)/%_static.o: %.c
116+
$(V_CC) -c $(STATIC_CFLAGS) -O3 -o $@ $<
117+
118+
$(NIF_LIB_FILE): $(NIF_STATIC_OBJ_FILES)
119+
$(V_AR) $(AR_FLAGS) $(AR_OUT)$@ $(NIF_STATIC_OBJ_FILES)
120+
$(V_RANLIB) $@
121+
122+
$(NIF_SHARED_OBJ_FILE): $(NIF_OBJ_FILES)
123+
$(V_LD) $(LDFLAGS) -o $(NIF_SHARED_OBJ_FILE) $(NIF_OBJ_FILES) $(CLIB_FLAGS) $(LIBS)
124+
125+
# ----------------------------------------------------
126+
# Release Target
127+
# ----------------------------------------------------
128+
include $(ERL_TOP)/make/otp_release_targets.mk
129+
130+
release_spec: opt
131+
$(INSTALL_DIR) "$(RELSYSDIR)/priv/lib"
132+
$(INSTALL_PROGRAM) $(NIF_SHARED_OBJ_FILE) "$(RELSYSDIR)/priv/lib"
133+
$(INSTALL_DIR) "$(RELSYSDIR)/c_src"
134+
$(INSTALL_DATA) *.c "$(RELSYSDIR)/c_src"
135+
136+
release_docs_spec:
137+

lib/kernel/c_src/file_nif.c

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/*
2+
* %CopyrightBegin%
3+
*
4+
* Copyright Ericsson AB 2002-2023. All Rights Reserved.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*
18+
* %CopyrightEnd%
19+
*
20+
*/
21+
#include "erl_nif.h"
22+
#include <stdio.h>
23+
#include <fcntl.h>
24+
#include <unistd.h>
25+
#include <sys/stat.h>
26+
#include <string.h>
27+
#include <errno.h>
28+
#include <sys/syscall.h>
29+
30+
static ERL_NIF_TERM copy_file_range_available_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
31+
ssize_t ret = syscall(SYS_copy_file_range, -1, NULL, -1, NULL, 0, 0);
32+
33+
if (ret == -1 && errno == ENOSYS) {
34+
return enif_make_atom(env, "false");
35+
} else {
36+
return enif_make_atom(env, "true");
37+
}
38+
}
39+
40+
static ERL_NIF_TERM copy_file_range_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
41+
int fd_in, fd_out;
42+
off64_t len, ret;
43+
struct stat stat;
44+
ErlNifBinary file_path_in, file_path_out;
45+
char atom_name[256];
46+
47+
if (!enif_inspect_binary(env, argv[0], &file_path_in) ||
48+
!enif_inspect_binary(env, argv[1], &file_path_out)) {
49+
return enif_make_badarg(env);
50+
}
51+
52+
// Check if the third argument is an atom
53+
if (enif_get_atom(env, argv[2], atom_name, sizeof(atom_name), ERL_NIF_LATIN1)) {
54+
if (strcmp(atom_name, "infinity") == 0) {
55+
if ((fd_in = open((char *)file_path_in.data, O_RDONLY)) != -1 && fstat(fd_in, &stat) != -1) {
56+
len = stat.st_size;
57+
close(fd_in);
58+
} else {
59+
return enif_make_badarg(env);
60+
}
61+
} else {
62+
return enif_make_badarg(env);
63+
}
64+
}
65+
// Check if the third argument is an integer
66+
else if (!enif_get_int64(env, argv[2], &len)) {
67+
return enif_make_tuple2(env, enif_make_atom(env, "error"), enif_make_atom(env, "badarg"));
68+
}
69+
70+
if (len < 0) {
71+
return enif_make_badarg(env);
72+
}
73+
74+
fd_in = open((char *)file_path_in.data, O_RDONLY);
75+
if (fd_in == -1) {
76+
return enif_make_tuple2(env, enif_make_atom(env, "error"), enif_make_atom(env, "badarg"));
77+
}
78+
79+
fd_out = open((char *)file_path_out.data, O_CREAT | O_WRONLY | O_TRUNC, 0644);
80+
if (fd_out == -1) {
81+
close(fd_in);
82+
return enif_make_tuple2(env, enif_make_atom(env, "error"), enif_make_atom(env, "badarg"));
83+
}
84+
85+
// May Linux distros still don't include copy_file_range in their
86+
// libc implementations, so we need to make a direct syscall.
87+
ret = syscall(SYS_copy_file_range, fd_in, NULL, fd_out, NULL, len, 0);
88+
89+
if (ret == -1) {
90+
return enif_make_tuple2(env, enif_make_atom(env, "error"), enif_make_atom(env, "copy_file_range syscall failed"));
91+
} else {
92+
return enif_make_tuple2(env, enif_make_atom(env, "ok"), enif_make_uint(env, (unsigned int)ret));
93+
}
94+
}
95+
96+
static ErlNifFunc nif_funcs[] = {
97+
{"copy_file_range_available", 0, copy_file_range_available_nif},
98+
{"copy_file_range_nif", 3, copy_file_range_nif},
99+
};
100+
101+
ERL_NIF_INIT(file_nif, nif_funcs, NULL, NULL, NULL, NULL)
102+

lib/kernel/src/Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,8 @@ MODULES = \
151151
raw_file_io_deflate \
152152
raw_file_io_delayed \
153153
raw_file_io_list \
154-
wrap_log_reader
154+
wrap_log_reader \
155+
file_nif
155156

156157
HRL_FILES= ../include/file.hrl ../include/inet.hrl ../include/inet_sctp.hrl \
157158
../include/dist.hrl ../include/dist_util.hrl \

lib/kernel/src/file.erl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -907,7 +907,10 @@ copy_int(Source, {_DestName, DestOpts} = Dest, Length)
907907
%% Both must be bare filenames. If they are not,
908908
%% the filename check in the copy operation will yell.
909909
copy_int(Source, Dest, Length) ->
910-
copy_int({Source, []}, {Dest, []}, Length).
910+
case file_nif:copy_file_range_available() of
911+
true -> file_nif:copy_file_range(Source, Dest, Length);
912+
false -> copy_int({Source, []}, {Dest, []}, Length)
913+
end.
911914

912915

913916

lib/kernel/src/file_nif.erl

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
%%
2+
%% %CopyrightBegin%
3+
%%
4+
%% Copyright Ericsson AB 2002-2023. All Rights Reserved.
5+
%%
6+
%% Licensed under the Apache License, Version 2.0 (the "License");
7+
%% you may not use this file except in compliance with the License.
8+
%% You may obtain a copy of the License at
9+
%%
10+
%% http://www.apache.org/licenses/LICENSE-2.0
11+
%%
12+
%% Unless required by applicable law or agreed to in writing, software
13+
%% distributed under the License is distributed on an "AS IS" BASIS,
14+
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
%% See the License for the specific language governing permissions and
16+
%% limitations under the License.
17+
%%
18+
%% %CopyrightEnd%
19+
%%
20+
%%
21+
-module(file_nif).
22+
-export([copy_file_range_available/0, copy_file_range_nif/3, copy_file_range/3]).
23+
24+
-nifs([copy_file_range_available/0, copy_file_range_nif/3]).
25+
-on_load(load_nif/0).
26+
27+
-define(FILE_NIF_VSN,1).
28+
29+
load_nif() ->
30+
LibBaseName = "file_nif",
31+
PrivDir = code:priv_dir(kernel),
32+
LibName = case erlang:system_info(build_type) of
33+
opt ->
34+
LibBaseName;
35+
Type ->
36+
LibTypeName = LibBaseName ++ "." ++ atom_to_list(Type),
37+
case (filelib:wildcard(
38+
filename:join(
39+
[PrivDir,
40+
"lib",
41+
LibTypeName ++ "*"]),
42+
erl_prim_loader) /= []) orelse
43+
(filelib:wildcard(
44+
filename:join(
45+
[PrivDir,
46+
"lib",
47+
erlang:system_info(system_architecture),
48+
LibTypeName ++ "*"]),
49+
erl_prim_loader) /= []) of
50+
true -> LibTypeName;
51+
false -> LibBaseName
52+
end
53+
end,
54+
Lib = filename:join([PrivDir, "lib", LibName]),
55+
Status = case erlang:load_nif(Lib, ?FILE_NIF_VSN) of
56+
ok -> ok;
57+
{error, {load_failed, _}}=Error1 ->
58+
ArchLibDir =
59+
filename:join([PrivDir, "lib",
60+
erlang:system_info(system_architecture)]),
61+
Candidate =
62+
filelib:wildcard(
63+
filename:join([ArchLibDir,LibName ++ "*" ]),
64+
erl_prim_loader),
65+
case Candidate of
66+
[] -> Error1;
67+
_ ->
68+
ArchLib = filename:join([ArchLibDir, LibName]),
69+
erlang:load_nif(ArchLib, ?FILE_NIF_VSN)
70+
end;
71+
Error1 -> Error1
72+
end,
73+
case Status of
74+
ok -> ok;
75+
{error, {E, Str}} ->
76+
error_logger:error_msg("Unable to load kernel nif library. "
77+
"Failed with error:~n\"~p, ~s\"~n",[E,Str]),
78+
Status
79+
end.
80+
81+
82+
83+
copy_file_range(Source, Dest, Length)
84+
when is_binary(Source), is_binary(Dest),
85+
is_integer(Length), Length >= 0 ->
86+
copy_file_range_nif(Source, Dest, Length);
87+
88+
copy_file_range(Source, Dest, Length)
89+
when is_binary(Source), is_binary(Dest),
90+
is_atom(Length) ->
91+
copy_file_range_nif(Source, Dest, Length);
92+
93+
copy_file_range(Source, Dest, Length)
94+
when is_list(Source), is_list(Dest) ->
95+
copy_file_range(list_to_binary(Source), list_to_binary(Dest), Length);
96+
97+
copy_file_range(Source, Dest, Length)
98+
when is_list(Source), is_binary(Dest) ->
99+
copy_file_range(list_to_binary(Source), Dest, Length);
100+
101+
copy_file_range(Source, Dest, Length)
102+
when is_binary(Source), is_list(Dest) ->
103+
copy_file_range(Source, list_to_binary(Dest), Length).
104+
105+
106+
copy_file_range_available() ->
107+
erlang:nif_error({nif_not_loaded,module,?MODULE,line,?LINE}).
108+
copy_file_range_nif(_,_,_) ->
109+
erlang:nif_error({nif_not_loaded,module,?MODULE,line,?LINE}).

lib/kernel/src/kernel.app.src

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,8 @@
122122
seq_trace,
123123
socket,
124124
standard_error,
125-
wrap_log_reader]},
125+
wrap_log_reader,
126+
file_nif]},
126127
{registered, [application_controller,
127128
erl_reply,
128129
auth,

0 commit comments

Comments
 (0)