aboutsummaryrefslogtreecommitdiffstats
path: root/erts/preloaded/src/prim_file.erl
diff options
context:
space:
mode:
Diffstat (limited to 'erts/preloaded/src/prim_file.erl')
-rw-r--r--erts/preloaded/src/prim_file.erl2015
1 files changed, 679 insertions, 1336 deletions
diff --git a/erts/preloaded/src/prim_file.erl b/erts/preloaded/src/prim_file.erl
index ab5359ebbc..5fc22bc582 100644
--- a/erts/preloaded/src/prim_file.erl
+++ b/erts/preloaded/src/prim_file.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -19,147 +19,45 @@
%%
-module(prim_file).
-%% Interface module to the file driver.
+-export([on_load/0]).
+-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]).
+%% OTP internal.
+-export([ipread_s32bu_p32bu/3, sendfile/8, altname/1, get_handle/1]).
-%%% Interface towards a single file's contents. Uses ?FD_DRV.
+-export([read_file/1, write_file/2]).
-%% Generic file contents operations
--export([open/2, close/1, datasync/1, sync/1, advise/4, position/2, truncate/1,
- write/2, pwrite/2, pwrite/3, read/2, read_line/1, pread/2, pread/3,
- copy/3, sendfile/8, allocate/3]).
+-export([read_link/1, read_link_all/1,
+ read_link_info/1, read_link_info/2,
+ read_file_info/1, read_file_info/2,
+ write_file_info/2, write_file_info/3]).
-%% Specialized file operations
--export([open/1, open/3]).
--export([read_file/1, read_file/2, write_file/2]).
--export([ipread_s32bu_p32bu/3]).
+-export([list_dir/1, list_dir_all/1]).
+-export([get_cwd/0, get_cwd/1, set_cwd/1,
+ delete/1, rename/2,
+ make_dir/1, del_dir/1,
+ make_link/2, make_symlink/2]).
-
-%%% Interface towards file system and metadata. Uses ?DRV.
-
-%% Takes an optional port (opens a ?DRV port per default) as first argument.
--export([get_cwd/0, get_cwd/1, get_cwd/2,
- set_cwd/1, set_cwd/2,
- delete/1, delete/2,
- rename/2, rename/3,
- make_dir/1, make_dir/2,
- del_dir/1, del_dir/2,
- read_file_info/1, read_file_info/2, read_file_info/3,
- altname/1, altname/2,
- write_file_info/2, write_file_info/3, write_file_info/4,
- make_link/2, make_link/3,
- make_symlink/2, make_symlink/3,
- read_link/1, read_link/2, read_link_all/1, read_link_all/2,
- read_link_info/1, read_link_info/2, read_link_info/3,
- list_dir/1, list_dir/2, list_dir_all/1, list_dir_all/2]).
-%% How to start and stop the ?DRV port.
--export([start/0, stop/1]).
-
-%% Debug exports
--export([open_int/4, open_mode/1, open_mode/4]).
-
-%%%-----------------------------------------------------------------
-%%% Includes and defines
-
--include("file.hrl").
-
--define(DRV, "efile").
--define(FD_DRV, "efile").
-
+-define(MIN_READLINE_SIZE, 256).
-define(LARGEFILESIZE, (1 bsl 63)).
-%% Driver commands
--define(FILE_OPEN, 1).
--define(FILE_READ, 2).
--define(FILE_LSEEK, 3).
--define(FILE_WRITE, 4).
--define(FILE_FSTAT, 5).
--define(FILE_PWD, 6).
--define(FILE_READDIR, 7).
--define(FILE_CHDIR, 8).
--define(FILE_FSYNC, 9).
--define(FILE_MKDIR, 10).
--define(FILE_DELETE, 11).
--define(FILE_RENAME, 12).
--define(FILE_RMDIR, 13).
--define(FILE_TRUNCATE, 14).
--define(FILE_READ_FILE, 15).
--define(FILE_WRITE_INFO, 16).
--define(FILE_LSTAT, 19).
--define(FILE_READLINK, 20).
--define(FILE_LINK, 21).
--define(FILE_SYMLINK, 22).
--define(FILE_CLOSE, 23).
--define(FILE_PWRITEV, 24).
--define(FILE_PREADV, 25).
--define(FILE_SETOPT, 26).
--define(FILE_IPREAD, 27).
--define(FILE_ALTNAME, 28).
--define(FILE_READ_LINE, 29).
--define(FILE_FDATASYNC, 30).
--define(FILE_ADVISE, 31).
--define(FILE_SENDFILE, 32).
--define(FILE_ALLOCATE, 33).
-
-%% Driver responses
--define(FILE_RESP_OK, 0).
--define(FILE_RESP_ERROR, 1).
--define(FILE_RESP_DATA, 2).
--define(FILE_RESP_NUMBER, 3).
--define(FILE_RESP_INFO, 4).
--define(FILE_RESP_NUMERR, 5).
--define(FILE_RESP_LDATA, 6).
--define(FILE_RESP_N2DATA, 7).
--define(FILE_RESP_EOF, 8).
--define(FILE_RESP_FNAME, 9).
--define(FILE_RESP_ALL_DATA, 10).
--define(FILE_RESP_LFNAME, 11).
-
-%% Open modes for the driver's open function.
--define(EFILE_MODE_READ, 1).
--define(EFILE_MODE_WRITE, 2).
--define(EFILE_MODE_READ_WRITE, 3).
--define(EFILE_MODE_APPEND, 4).
--define(EFILE_COMPRESSED, 8).
--define(EFILE_MODE_EXCL, 16).
-%% Note: bit 5 (32) is used internally for VxWorks
--define(EFILE_MODE_SYNC, 64).
-
-%% Use this mask to get just the mode bits to be passed to the driver.
--define(EFILE_MODE_MASK, 127).
-
-%% Seek modes for the driver's seek function.
--define(EFILE_SEEK_SET, 0).
--define(EFILE_SEEK_CUR, 1).
--define(EFILE_SEEK_END, 2).
-
-%% Options
--define(FILE_OPT_DELAYED_WRITE, 0).
--define(FILE_OPT_READ_AHEAD, 1).
-
-%% IPREAD variants
--define(IPREAD_S32BU_P32BU, 0).
-
-%% POSIX file advises
--define(POSIX_FADV_NORMAL, 0).
--define(POSIX_FADV_RANDOM, 1).
--define(POSIX_FADV_SEQUENTIAL, 2).
--define(POSIX_FADV_WILLNEED, 3).
--define(POSIX_FADV_DONTNEED, 4).
--define(POSIX_FADV_NOREUSE, 5).
-
-%% Sendfile flags
--define(EFILE_SENDFILE_USE_THREADS, 1).
+-export([copy/3]).
+-include("file_int.hrl").
+
+-type prim_file_ref() :: term().
%%% BIFs
-export([internal_name2native/1,
internal_native2name/1,
internal_normalize_utf8/1,
- is_translatable/1]).
+ is_translatable/1]).
-type prim_file_name() :: string() | unicode:unicode_binary().
-type prim_file_name_error() :: 'error' | 'ignore' | 'warning'.
@@ -185,1316 +83,761 @@ internal_normalize_utf8(_) ->
is_translatable(_) ->
erlang:nif_error(undefined).
-%%% End of BIFs
-
-%%%-----------------------------------------------------------------
-%%% Functions operating on a file through a handle. ?FD_DRV.
-%%%
-%%% Generic file contents operations.
-%%%
-%%% Supposed to be called by applications through module file.
-
-
-%% Opens a file using the driver port Port. Returns {error, Reason}
-%% | {ok, FileDescriptor}
-open(Port, File, ModeList) when is_port(Port),
- (is_list(File) orelse is_binary(File)),
- is_list(ModeList) ->
- case open_mode(ModeList) of
- {Mode, _Portopts, _Setopts} ->
- open_int(Port, File, Mode, []);
- Reason ->
- {error, Reason}
- end;
-open(_,_,_) ->
- {error, badarg}.
-
-%% Opens a file. Returns {error, Reason} | {ok, FileDescriptor}.
-open(File, ModeList) when (is_list(File) orelse is_binary(File)),
- is_list(ModeList) ->
- case open_mode(ModeList) of
- {Mode, Portopts, Setopts} ->
- open_int({?FD_DRV, Portopts},File, Mode, Setopts);
- Reason ->
- {error, Reason}
- end;
-open(_, _) ->
- {error, badarg}.
+%% This is a janitor process used to close files whose controlling process has
+%% died. The emulator will be torn down if this is killed.
+delayed_close_loop() ->
+ receive
+ {close, FRef} when is_reference(FRef) -> delayed_close_nif(FRef);
+ _ -> ok
+ end,
+ delayed_close_loop().
-%% Opens a port that can be used for open/3 or read_file/2.
-%% Returns {ok, Port} | {error, Reason}.
-open(Portopts) when is_list(Portopts) ->
- drv_open(?FD_DRV, [binary|Portopts]);
-open(_) ->
- {error, badarg}.
+%%
-open_int({Driver, Portopts}, File, Mode, Setopts) ->
- case drv_open(Driver, Portopts) of
- {ok, Port} ->
- open_int(Port, File, Mode, Setopts);
- {error, _} = Error ->
- Error
- end;
-open_int(Port, File, Mode, Setopts) ->
- M = Mode band ?EFILE_MODE_MASK,
- case drv_command(Port, [<<?FILE_OPEN, M:32>>, pathname(File)]) of
- {ok, Number} ->
- open_int_setopts(Port, Number, Setopts);
- Error ->
- drv_close(Port),
- Error
- end.
+%% Returns {error, Reason} | {ok, BytesCopied}
+copy(#file_descriptor{module = ?MODULE} = Source,
+ #file_descriptor{module = ?MODULE} = Dest,
+ Length)
+ when is_integer(Length), Length >= 0;
+ is_atom(Length) ->
+ %% XXX Should be moved down to the driver for optimization.
+ file:copy_opened(Source, Dest, Length).
-open_int_setopts(Port, Number, []) ->
- {ok, #file_descriptor{module = ?MODULE, data = {Port, Number}}};
-open_int_setopts(Port, Number, [Cmd | Tail]) ->
- case drv_command(Port, Cmd) of
- ok ->
- open_int_setopts(Port, Number, Tail);
- Error ->
- drv_close(Port),
- Error
+on_load() ->
+ Pid = spawn(fun() ->
+ process_flag(trap_exit, true),
+ delayed_close_loop()
+ end),
+ true = register(erts_prim_file, Pid),
+ ok = erlang:load_nif(atom_to_list(?MODULE), Pid).
+
+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
+ %% "raw files."
+ try open_nif(encode_path(Name), Modes) of
+ {ok, Ref} -> {ok, make_fd(Ref, Modes)};
+ {error, Reason} -> {error, Reason}
+ catch
+ error:badarg -> {error, badarg}
end.
+make_fd(FRef, Modes) ->
+ #file_descriptor{module = ?MODULE, data = build_fd_data(FRef, Modes) }.
-
-%% Returns ok.
-
-close(#file_descriptor{module = ?MODULE, data = {Port, _}}) ->
- case drv_command(Port, <<?FILE_CLOSE>>) of
- ok ->
- drv_close(Port);
- Error ->
- Error
- end;
-%% Closes a port opened with open/1.
-close(Port) when is_port(Port) ->
- drv_close(Port).
-
--define(ADVISE(Offs, Len, Adv),
- <<?FILE_ADVISE, Offs:64/signed, Len:64/signed,
- Adv:32/signed>>).
-
-%% Returns {error, Reason} | ok.
-advise(#file_descriptor{module = ?MODULE, data = {Port, _}},
- Offset, Length, Advise) ->
- case Advise of
- normal ->
- Cmd = ?ADVISE(Offset, Length, ?POSIX_FADV_NORMAL),
- drv_command(Port, Cmd);
- random ->
- Cmd = ?ADVISE(Offset, Length, ?POSIX_FADV_RANDOM),
- drv_command(Port, Cmd);
- sequential ->
- Cmd = ?ADVISE(Offset, Length, ?POSIX_FADV_SEQUENTIAL),
- drv_command(Port, Cmd);
- will_need ->
- Cmd = ?ADVISE(Offset, Length, ?POSIX_FADV_WILLNEED),
- drv_command(Port, Cmd);
- dont_need ->
- Cmd = ?ADVISE(Offset, Length, ?POSIX_FADV_DONTNEED),
- drv_command(Port, Cmd);
- no_reuse ->
- Cmd = ?ADVISE(Offset, Length, ?POSIX_FADV_NOREUSE),
- drv_command(Port, Cmd);
- _ ->
- {error, einval}
+close(Fd) ->
+ try
+ #{ handle := FRef } = get_fd_data(Fd),
+ close_nif(FRef)
+ catch
+ error:badarg -> {error, badarg}
end.
-%% Returns {error, Reason} | ok.
-allocate(#file_descriptor{module = ?MODULE, data = {Port, _}}, Offset, Length) ->
- Cmd = <<?FILE_ALLOCATE, Offset:64/signed, Length:64/signed>>,
- drv_command(Port, Cmd).
-
-%% Returns {error, Reason} | ok.
-write(#file_descriptor{module = ?MODULE, data = {Port, _}}, Bytes) ->
- case drv_command_nt(Port, [?FILE_WRITE,erlang:dt_prepend_vm_tag_data(Bytes)],undefined) of
- {ok, _Size} ->
- ok;
- Error ->
- Error
+read(Fd, Size) ->
+ try
+ #{ handle := FRef,
+ r_ahead_size := RASz,
+ r_buffer := RBuf } = get_fd_data(Fd),
+ read_1(FRef, RBuf, prim_buffer:size(RBuf), RASz, Size)
+ catch
+ error:badarg -> {error, badarg}
end.
-%% Returns ok | {error, {WrittenCount, Reason}}
-pwrite(#file_descriptor{module = ?MODULE, data = {Port, _}}, L)
- when is_list(L) ->
- pwrite_int(Port, L, 0, [], []).
-
-pwrite_int(_, [], 0, [], []) ->
- ok;
-pwrite_int(Port, [], N, Spec, Data) ->
- Header = list_to_binary([?FILE_PWRITEV, erlang:dt_prepend_vm_tag_data(<<N:32>>) | reverse(Spec)]),
- case drv_command_nt(Port, [Header | reverse(Data)], undefined) of
- {ok, _Size} ->
- ok;
- Error ->
- Error
- end;
-pwrite_int(Port, [{Offs, Bytes} | T], N, Spec, Data)
- when is_integer(Offs) ->
- if
- -(?LARGEFILESIZE) =< Offs, Offs < ?LARGEFILESIZE ->
- pwrite_int(Port, T, N, Spec, Data, Offs, Bytes);
- true ->
- {error, einval}
+-spec read_1(FRef, RBuf, RBufSz, RASz, RSz) -> Result when
+ FRef :: prim_file_ref(),
+ RBuf :: term(),
+ RBufSz :: non_neg_integer(),
+ RASz :: non_neg_integer(),
+ RSz :: non_neg_integer(),
+ Result :: eof | {ok, binary()} | {error, Reason :: atom()}.
+read_1(_FRef, RBuf, RBufSz, _RASz, RSz) when RBufSz >= RSz ->
+ {ok, prim_buffer:read(RBuf, RSz)};
+read_1(FRef, RBuf, RBufSz, RASz, RSz) when RBufSz > 0 ->
+ Buffered = prim_buffer:read(RBuf, RBufSz),
+ case read_1(FRef, RBuf, 0, RASz, RSz - RBufSz) of
+ {ok, Data} ->
+ {ok, <<Buffered/binary, Data/binary>>};
+ eof ->
+ {ok, Buffered};
+ {error, Reason} ->
+ {error, Reason}
end;
-pwrite_int(_, [_|_], _N, _Spec, _Data) ->
- {error, badarg}.
+read_1(FRef, RBuf, RBufSz, RASz, RSz) when RBufSz =:= 0 ->
+ case read_nif(FRef, RASz + RSz) of
+ {ok, Data} when byte_size(Data) > RSz ->
+ {First, Rest} = split_binary(Data, RSz),
+ prim_buffer:write(RBuf, [Rest]),
+ {ok, First};
+ {ok, Data} when byte_size(Data) =< RSz ->
+ {ok, Data};
+ eof ->
+ eof;
+ {error, Reason} ->
+ {error, Reason}
+ end.
-pwrite_int(Port, T, N, Spec, Data, Offs, Bin)
- when is_binary(Bin) ->
- Size = byte_size(Bin),
- pwrite_int(Port, T, N+1,
- [<<Offs:64/signed, Size:64>> | Spec],
- [Bin | Data]);
-pwrite_int(Port, T, N, Spec, Data, Offs, Bytes) ->
- try list_to_binary(Bytes) of
- Bin ->
- pwrite_int(Port, T, N, Spec, Data, Offs, Bin)
+read_line(Fd) ->
+ try
+ #{ handle := FRef,
+ r_ahead_size := RASz,
+ r_buffer := RBuf } = get_fd_data(Fd),
+ SearchResult = prim_buffer:find_byte_index(RBuf, $\n),
+ LineSize = max(?MIN_READLINE_SIZE, RASz),
+ read_line_1(FRef, RBuf, SearchResult, LineSize)
catch
- error:Reason ->
- {error, Reason}
+ error:badarg -> {error, badarg}
end.
-
-
-%% Returns {error, Reason} | ok.
-pwrite(#file_descriptor{module = ?MODULE, data = {Port, _}}, Offs, Bytes)
- when is_integer(Offs) ->
- if
- -(?LARGEFILESIZE) =< Offs, Offs < ?LARGEFILESIZE ->
- case pwrite_int(Port, [], 0, [], [], Offs, Bytes) of
- {error, {_, Reason}} ->
- {error, Reason};
- Result ->
- Result
- end;
- true ->
- {error, einval}
+-spec read_line_1(FRef, RBuf, SearchResult, LineSize) -> Result when
+ FRef :: prim_file_ref(),
+ RBuf :: term(),
+ SearchResult :: not_found | {ok, non_neg_integer()},
+ LineSize :: non_neg_integer(),
+ Result :: eof | {ok, binary()} | {error, Reason :: atom()}.
+read_line_1(FRef, RBuf, not_found, LineSize) ->
+ case read_nif(FRef, LineSize) of
+ {ok, Data} ->
+ prim_buffer:write(RBuf, [Data]),
+ SearchResult = prim_buffer:find_byte_index(RBuf, $\n),
+ read_line_1(FRef, RBuf, SearchResult, LineSize);
+ eof ->
+ case prim_buffer:size(RBuf) of
+ Size when Size > 0 -> {ok, prim_buffer:read(RBuf, Size)};
+ Size when Size =:= 0 -> eof
+ end;
+ {error, Reason} ->
+ {error, Reason}
end;
-pwrite(#file_descriptor{module = ?MODULE}, _, _) ->
- {error, badarg}.
-
+read_line_1(_FRef, RBuf, {ok, LFIndex}, _LineSize) ->
+ %% Translate CRLF into just LF, completely ignoring which encoding is used.
+ CRIndex = (LFIndex - 1),
+ case prim_buffer:read(RBuf, LFIndex + 1) of
+ <<Line:CRIndex/binary, "\r\n">> -> {ok, <<Line/binary, "\n">>};
+ Line -> {ok, Line}
+ end.
-%% Returns {error, Reason} | ok.
-datasync(#file_descriptor{module = ?MODULE, data = {Port, _}}) ->
- drv_command(Port, [?FILE_FDATASYNC]).
-
-%% Returns {error, Reason} | ok.
-sync(#file_descriptor{module = ?MODULE, data = {Port, _}}) ->
- drv_command(Port, [?FILE_FSYNC]).
-
-%% Returns {ok, Data} | eof | {error, Reason}.
-read_line(#file_descriptor{module = ?MODULE, data = {Port, _}}) ->
- case drv_command(Port, <<?FILE_READ_LINE>>) of
- {ok, {0, _Data}} ->
- eof;
- {ok, {_Size, Data}} ->
- {ok, Data};
- {error, enomem} ->
- erlang:garbage_collect(),
- case drv_command(Port, <<?FILE_READ_LINE>>) of
- {ok, {0, _Data}} ->
- eof;
- {ok, {_Size, Data}} ->
- {ok, Data};
- Other ->
- Other
- end;
- Error ->
- Error
+write(Fd, IOData) ->
+ try
+ #{ handle := FRef } = get_fd_data(Fd),
+ reset_write_position(Fd),
+ write_1(FRef, erlang:iolist_to_iovec(IOData))
+ catch
+ error:badarg -> {error, badarg}
end.
-
-%% Returns {ok, Data} | eof | {error, Reason}.
-read(#file_descriptor{module = ?MODULE, data = {Port, _}}, Size)
- when is_integer(Size), 0 =< Size ->
- if
- Size < ?LARGEFILESIZE ->
- case drv_command(Port, <<?FILE_READ, Size:64>>) of
- {ok, {0, _Data}} when Size =/= 0 ->
- eof;
- {ok, {_Size, Data}} ->
- {ok, Data};
- {error, enomem} ->
- %% Garbage collecting here might help if
- %% the current processes have some old binaries left.
- erlang:garbage_collect(),
- case drv_command(Port, <<?FILE_READ, Size:64>>) of
- {ok, {0, _Data}} when Size =/= 0 ->
- eof;
- {ok, {_Size, Data}} ->
- {ok, Data};
- Other ->
- Other
- end;
- Error ->
- Error
- end;
- true ->
- {error, einval}
+write_1(FRef, IOVec) ->
+ case write_nif(FRef, IOVec) of
+ {continue, Remainder} ->
+ write_1(FRef, Remainder);
+ ok ->
+ ok;
+ {error, Reason} ->
+ {error, Reason}
end.
-%% Returns {ok, [Data|eof, ...]} | {error, Reason}
-pread(#file_descriptor{module = ?MODULE, data = {Port, _}}, L)
- when is_list(L) ->
- pread_int(Port, L, 0, []).
-
-pread_int(_, [], 0, []) ->
- {ok, []};
-pread_int(Port, [], N, Spec) ->
- drv_command_nt(Port, [?FILE_PREADV, erlang:dt_prepend_vm_tag_data(<<0:32, N:32>>) | reverse(Spec)],undefined);
-pread_int(Port, [{Offs, Size} | T], N, Spec)
- when is_integer(Offs), is_integer(Size), 0 =< Size ->
- if
- -(?LARGEFILESIZE) =< Offs, Offs < ?LARGEFILESIZE,
- Size < ?LARGEFILESIZE ->
- pread_int(Port, T, N+1, [<<Offs:64/signed, Size:64>> | Spec]);
- true ->
- {error, einval}
- end;
-pread_int(_, [_|_], _N, _Spec) ->
- {error, badarg}.
-
-
-
-%% Returns {ok, Data} | eof | {error, Reason}.
-pread(#file_descriptor{module = ?MODULE, data = {Port, _}}, Offs, Size)
- when is_integer(Offs), is_integer(Size), 0 =< Size ->
- if
- -(?LARGEFILESIZE) =< Offs, Offs < ?LARGEFILESIZE,
- Size < ?LARGEFILESIZE ->
- case drv_command_nt(Port,
- [?FILE_PREADV, erlang:dt_prepend_vm_tag_data(<<0:32, 1:32,
- Offs:64/signed, Size:64>>)], undefined) of
- {ok, [eof]} ->
- eof;
- {ok, [Data]} ->
- {ok, Data};
- Error ->
- Error
- end;
- true ->
- {error, einval}
- end;
-pread(#file_descriptor{module = ?MODULE, data = {_, _}}, _, _) ->
- {error, badarg}.
-
-
-
-%% Returns {ok, Position} | {error, Reason}.
-position(#file_descriptor{module = ?MODULE, data = {Port, _}}, At) ->
- case lseek_position(At) of
- {Offs, Whence}
- when -(?LARGEFILESIZE) =< Offs, Offs < ?LARGEFILESIZE ->
- drv_command(Port, <<?FILE_LSEEK, Offs:64/signed, Whence:32>>);
- {_, _} ->
- {error, einval};
- Reason ->
- {error, Reason}
+truncate(Fd) ->
+ try
+ #{ handle := FRef } = get_fd_data(Fd),
+ reset_write_position(Fd),
+ truncate_nif(FRef)
+ catch
+ error:badarg -> {error, badarg}
end.
-%% Returns {error, Reason} | ok.
-truncate(#file_descriptor{module = ?MODULE, data = {Port, _}}) ->
- drv_command(Port, <<?FILE_TRUNCATE>>).
-
-
-
-%% Returns {error, Reason} | {ok, BytesCopied}
-copy(#file_descriptor{module = ?MODULE} = Source,
- #file_descriptor{module = ?MODULE} = Dest,
- Length)
- when is_integer(Length), Length >= 0;
- is_atom(Length) ->
- %% XXX Should be moved down to the driver for optimization.
- file:copy_opened(Source, Dest, Length).
-
-
-
-ipread_s32bu_p32bu(#file_descriptor{module = ?MODULE,
- data = {_, _}} = Handle,
- Offs,
- Infinity) when is_atom(Infinity) ->
- ipread_s32bu_p32bu(Handle, Offs, (1 bsl 31)-1);
-ipread_s32bu_p32bu(#file_descriptor{module = ?MODULE, data = {Port, _}},
- Offs,
- MaxSize)
- when is_integer(Offs), is_integer(MaxSize) ->
- if
- -(?LARGEFILESIZE) =< Offs, Offs < ?LARGEFILESIZE,
- 0 =< MaxSize, MaxSize < (1 bsl 31) ->
- drv_command(Port, <<?FILE_IPREAD, ?IPREAD_S32BU_P32BU,
- Offs:64, MaxSize:32>>);
- true ->
- {error, einval}
- end;
-ipread_s32bu_p32bu(#file_descriptor{module = ?MODULE, data = {_, _}},
- _Offs,
- _MaxSize) ->
- {error, badarg}.
+advise(Fd, Offset, Length, Advise) ->
+ try
+ #{ handle := FRef } = get_fd_data(Fd),
+ advise_nif(FRef, Offset, Length, Advise)
+ catch
+ error:badarg -> {error, badarg}
+ end.
+allocate(Fd, Offset, Length) ->
+ try
+ #{ handle := FRef } = get_fd_data(Fd),
+ allocate_nif(FRef, Offset, Length)
+ catch
+ error:badarg -> {error, badarg}
+ end.
+sync(Fd) ->
+ try
+ #{ handle := FRef } = get_fd_data(Fd),
+ sync_nif(FRef, 0)
+ catch
+ error:badarg -> {error, badarg}
+ end.
-%% Returns {ok, Contents} | {error, Reason}
-read_file(File) when (is_list(File) orelse is_binary(File)) ->
- case drv_open(?FD_DRV, [binary]) of
- {ok, Port} ->
- Result = read_file(Port, File),
- close(Port),
- Result;
- {error, _} = Error ->
- Error
- end;
-read_file(_) ->
- {error, badarg}.
+datasync(Fd) ->
+ try
+ #{ handle := FRef } = get_fd_data(Fd),
+ sync_nif(FRef, 1)
+ catch
+ error:badarg -> {error, badarg}
+ end.
-%% Takes a Port opened with open/1.
-read_file(Port, File) when is_port(Port),
- (is_list(File) orelse is_binary(File)) ->
- Cmd = [?FILE_READ_FILE | pathname(File)],
- case drv_command(Port, Cmd) of
- {error, enomem} ->
- %% It could possibly help to do a
- %% garbage collection here,
- %% if the file server has some references
- %% to binaries read earlier.
- erlang:garbage_collect(),
- drv_command(Port, Cmd);
- Result ->
- Result
+position(Fd, {cur, Offset}) ->
+ try
+ %% Adjust our current position according to how much we've read ahead.
+ #{ r_buffer := RBuf } = get_fd_data(Fd),
+ position_1(Fd, cur, Offset - prim_buffer:size(RBuf))
+ catch
+ error:badarg -> {error, badarg}
end;
-read_file(_,_) ->
- {error, badarg}.
-
-
-
-%% Returns {error, Reason} | ok.
-write_file(File, Bin) when (is_list(File) orelse is_binary(File)) ->
- case open(File, [binary, write]) of
- {ok, Handle} ->
- Result = write(Handle, Bin),
- close(Handle),
- Result;
- Error ->
- Error
+position(Fd, {Mark, Offset}) ->
+ try
+ position_1(Fd, Mark, Offset)
+ catch
+ error:badarg -> {error, badarg}
end;
-write_file(_, _) ->
- {error, badarg}.
+position(Fd, cur) -> position(Fd, {cur, 0});
+position(Fd, bof) -> position(Fd, {bof, 0});
+position(Fd, eof) -> position(Fd, {eof, 0});
+position(Fd, Offset) -> position(Fd, {bof, Offset}).
+position_1(Fd, Mark, Offset) ->
+ #{ handle := FRef, r_buffer := RBuf } = get_fd_data(Fd),
+ prim_buffer:wipe(RBuf),
+ seek_nif(FRef, Mark, Offset).
-%% Returns {error, Reason} | {ok, BytesCopied}
-%sendfile(_,_,_,_,_,_,_,_,_,_) ->
-% {error, enotsup};
-sendfile(#file_descriptor{module = ?MODULE, data = {Port, _}},
- Dest, Offset, Bytes, _ChunkSize, Headers, Trailers,
- Flags) ->
- case erlang:port_get_data(Dest) of
- Data when Data == inet_tcp; Data == inet6_tcp ->
- ok = inet:lock_socket(Dest,true),
- {ok, DestFD} = prim_inet:getfd(Dest),
- IntFlags = translate_sendfile_flags(Flags),
- try drv_command(Port, [<<?FILE_SENDFILE, DestFD:32,
- IntFlags:8,
- Offset:64/unsigned,
- Bytes:64/unsigned,
- (iolist_size(Headers)):32/unsigned,
- (iolist_size(Trailers)):32/unsigned>>,
- Headers,Trailers])
- after
- ok = inet:lock_socket(Dest,false)
- end;
- _Else ->
- {error,badarg}
+pread(Fd, Offset, Size) ->
+ try
+ #{ handle := FRef } = get_fd_data(Fd),
+ pread_nif(FRef, Offset, Size)
+ catch
+ error:badarg -> {error, badarg}
end.
-translate_sendfile_flags([{use_threads,true}|T]) ->
- ?EFILE_SENDFILE_USE_THREADS bor translate_sendfile_flags(T);
-translate_sendfile_flags([_|T]) ->
- translate_sendfile_flags(T);
-translate_sendfile_flags([]) ->
- 0.
-
-
-%%%-----------------------------------------------------------------
-%%% Functions operating on files without handle to the file. ?DRV.
-%%%
-%%% Supposed to be called by applications through module file.
-
-
-
-%% Returns {ok, Port}, the Port should be used as first argument in all
-%% the following functions. Returns {error, Reason} upon failure.
-start() ->
- drv_open(?DRV, [binary]).
-
-stop(Port) when is_port(Port) ->
- try erlang:port_close(Port) of
- _ ->
- ok
+pread(Fd, LocNums) ->
+ try
+ #{ handle := FRef } = get_fd_data(Fd),
+ pread_list(FRef, LocNums, [])
catch
- _:_ ->
- ok
+ error:badarg -> {error, badarg}
end.
+-spec pread_list(FRef, LocNums, ResultList) -> Result when
+ FRef :: prim_file_ref(),
+ LocNums :: list({Offset :: non_neg_integer(),
+ Size :: non_neg_integer()}),
+ ResultList :: list(eof | binary()),
+ Result :: {ok, ResultList} | {error, Reason :: atom()}.
+pread_list(_FRef, [], ResultList) ->
+ {ok, reverse_list(ResultList)};
+pread_list(FRef, [{Offset, Size} | Rest], ResultList) ->
+ case pread_nif(FRef, Offset, Size) of
+ {ok, Data} ->
+ pread_list(FRef, Rest, [Data | ResultList]);
+ eof ->
+ pread_list(FRef, Rest, [eof | ResultList]);
+ {error, Reason} ->
+ {error, Reason}
+ end.
+pwrite(Fd, Offset, IOData) ->
+ try
+ #{ handle := FRef, r_buffer := RBuf } = get_fd_data(Fd),
+ prim_buffer:wipe(RBuf),
+ pwrite_plain(FRef, Offset, erlang:iolist_to_iovec(IOData))
+ catch
+ error:badarg -> {error, badarg}
+ end.
+pwrite_plain(FRef, Offset, IOVec) ->
+ case pwrite_nif(FRef, Offset, IOVec) of
+ {continue, BytesWritten, Remainder} ->
+ pwrite_plain(FRef, Offset + BytesWritten, Remainder);
+ ok ->
+ ok;
+ {error, Reason} ->
+ {error, Reason}
+ end.
-%%% The following functions take an optional Port as first argument.
-%%% If the port is not supplied, a temporary one is opened and then
-%%% closed after the request has been performed.
-
-
-
-%% get_cwd/{0,1,2}
-
-get_cwd() ->
- get_cwd_int(0).
-
-get_cwd(Port) when is_port(Port) ->
- get_cwd_int(Port, 0);
-get_cwd([]) ->
- get_cwd_int(0);
-get_cwd([Letter, $: | _]) when $a =< Letter, Letter =< $z ->
- get_cwd_int(Letter - $a + 1);
-get_cwd([Letter, $: | _]) when $A =< Letter, Letter =< $Z ->
- get_cwd_int(Letter - $A + 1);
-get_cwd([_|_]) ->
- {error, einval};
-get_cwd(_) ->
- {error, badarg}.
-
-get_cwd(Port, []) when is_port(Port) ->
- get_cwd_int(Port, 0);
-get_cwd(Port, [Letter, $: | _])
- when is_port(Port), $a =< Letter, Letter =< $z ->
- get_cwd_int(Port, Letter - $a + 1);
-get_cwd(Port, [Letter, $: | _])
- when is_port(Port), $A =< Letter, Letter =< $Z ->
- get_cwd_int(Port, Letter - $A + 1);
-get_cwd(Port, [_|_]) when is_port(Port) ->
- {error, einval};
-get_cwd(_, _) ->
- {error, badarg}.
-
-get_cwd_int(Drive) ->
- get_cwd_int({?DRV, [binary]}, Drive).
-
-get_cwd_int(Port, Drive) ->
- drv_command(Port, <<?FILE_PWD, Drive>>,
- fun handle_fname_response/1).
-
-
-
-%% set_cwd/{1,2}
+pwrite(Fd, LocBytes) ->
+ try
+ #{ handle := FRef, r_buffer := RBuf } = get_fd_data(Fd),
+ prim_buffer:wipe(RBuf),
+ pwrite_list(FRef, LocBytes, 0)
+ catch
+ error:badarg -> {error, badarg}
+ end.
-set_cwd(Dir) ->
- set_cwd_int({?DRV, [binary]}, Dir).
+-spec pwrite_list(FRef, LocBytes, Successes) -> Result when
+ FRef :: prim_file_ref(),
+ LocBytes :: list({Offset :: non_neg_integer(),
+ IOData :: iodata()}),
+ Successes :: non_neg_integer(),
+ Result :: ok | {error, {Successes, Reason :: atom()}}.
+pwrite_list(_FRef, [], _Successes) ->
+ ok;
+pwrite_list(FRef, [{Offset, IOData} | Rest], Successes) ->
+ case pwrite_plain(FRef, Offset, erlang:iolist_to_iovec(IOData)) of
+ {error, Reason} -> {error, {Successes, Reason}};
+ ok -> pwrite_list(FRef, Rest, Successes + 1)
+ end.
-set_cwd(Port, Dir) when is_port(Port) ->
- set_cwd_int(Port, Dir).
+sendfile(Fd, Socket, Offset, Bytes, _ChunkSize, [], [], _Flags) ->
+ %% There's a very nasty race in here; if we die just prior to duplicating
+ %% the handle down in the sendfile call, it might get reused by something
+ %% entirely different and we'll leak unknown data to the socket until it
+ %% dies soon after.
+ %%
+ %% This bug was inherited from the old driver, except it was vulnerable to
+ %% the bug at any point and not just during setup.
+ %%
+ %% We'll have to live with this until we have a way to unambiguously
+ %% transfer things between drivers or NIFs. Current ideas all fall afoul
+ %% of the Two Generals problem.
+ try
+ advise(Fd, Offset, Bytes, sequential),
+ prim_inet:sendfile(Socket, get_handle(Fd), Offset, Bytes)
+ catch
+ error:badarg -> {error, badarg}
+ end;
+sendfile(_Fd, _Socket, _Offset, _Bytes, _ChunkSize, _Headers, _Trailers, _Flags) ->
+ {error, enotsup}.
-set_cwd_int(Port, Dir) when is_binary(Dir) ->
- case prim_file:is_translatable(Dir) of
- false ->
- {error, no_translation};
- true ->
- drv_command(Port, [?FILE_CHDIR, pathname(Dir)])
+%% Undocumented internal function that reads a data block with indirection.
+%%
+%% This is only used once in DETS and can easily be emulated with pread/2, but
+%% it's pretty performance-sensitive so we've implemented it down in the NIF to
+%% avoid excessive rescheduling.
+-spec ipread_s32bu_p32bu(Fd, Offset, MaxSize) -> Result when
+ Fd :: #file_descriptor{},
+ Offset :: non_neg_integer(),
+ MaxSize :: non_neg_integer() | infinity,
+ Result :: {ok, Size :: non_neg_integer(),
+ Pointer :: non_neg_integer(),
+ Data :: iodata() | eof} |
+ eof |
+ {error, Reason :: atom()}.
+ipread_s32bu_p32bu(Fd, Offset, Infinity) when is_atom(Infinity) ->
+ ipread_s32bu_p32bu(Fd, Offset, (1 bsl 31) - 1);
+ipread_s32bu_p32bu(Fd, Offset, MaxSize)
+ when is_integer(Offset), is_integer(MaxSize) ->
+ try
+ #{ handle := FRef } = get_fd_data(Fd),
+ ipread_s32bu_p32bu_nif(FRef, Offset, MaxSize)
+ catch
+ error:badarg -> {error, badarg}
end;
-set_cwd_int(Port, Dir) when is_list(Dir) ->
- drv_command(Port, [?FILE_CHDIR, pathname(Dir)]);
-set_cwd_int(_, _) ->
+ipread_s32bu_p32bu(_Fd, _Offset, _MaxSize) ->
{error, badarg}.
-
-
-
-%% delete/{1,2}
-
-delete(File) ->
- delete_int({?DRV, [binary]}, File).
-
-delete(Port, File) when is_port(Port) ->
- delete_int(Port, File).
-
-delete_int(Port, File) ->
- drv_command(Port, [?FILE_DELETE, pathname(File)]).
-
-
-
-%% rename/{2,3}
-
-rename(From, To) ->
- rename_int({?DRV, [binary]}, From, To).
-
-rename(Port, From, To) when is_port(Port) ->
- rename_int(Port, From, To).
-
-rename_int(Port, From, To) ->
- drv_command(Port, [?FILE_RENAME, pathname(From), pathname(To)]).
-
-
-
-%% make_dir/{1,2}
-
-make_dir(Dir) ->
- make_dir_int({?DRV, [binary]}, Dir).
-
-make_dir(Port, Dir) when is_port(Port) ->
- make_dir_int(Port, Dir).
-
-make_dir_int(Port, Dir) ->
- drv_command(Port, [?FILE_MKDIR, pathname(Dir)]).
-
-
-
-%% del_dir/{1,2}
-
-del_dir(Dir) ->
- del_dir_int({?DRV, [binary]}, Dir).
-
-del_dir(Port, Dir) when is_port(Port) ->
- del_dir_int(Port, Dir).
-
-del_dir_int(Port, Dir) ->
- drv_command(Port, [?FILE_RMDIR, pathname(Dir)]).
-
-
-
-%% read_file_info/{1,2,3}
-
-read_file_info(File) ->
- read_file_info_int({?DRV, [binary]}, File, local).
-
-read_file_info(Port, File) when is_port(Port) ->
- read_file_info_int(Port, File, local);
-read_file_info(File, Opts) ->
- read_file_info_int({?DRV, [binary]}, File, plgv(time, Opts, local)).
-
-read_file_info(Port, File, Opts) when is_port(Port) ->
- read_file_info_int(Port, File, plgv(time, Opts, local)).
-
-read_file_info_int(Port, File, TimeType) ->
+ipread_s32bu_p32bu_nif(_FRef, _Offset, _MaxSize) ->
+ erlang:nif_error(undef).
+
+%% Returns the binary representation of the underlying handle, for use in
+%% tricky operations like sendfile/8.
+-spec get_handle(Fd) -> Result when
+ Fd :: #file_descriptor{},
+ Result :: binary() | {error, Reason :: atom()}.
+get_handle(Fd) ->
try
- case drv_command(Port, [?FILE_FSTAT, pathname(File)]) of
- {ok, FI} -> {ok, FI#file_info{
- ctime = from_seconds(FI#file_info.ctime, TimeType),
- mtime = from_seconds(FI#file_info.mtime, TimeType),
- atime = from_seconds(FI#file_info.atime, TimeType)
- }};
- Error -> Error
- end
+ #{ handle := FRef } = get_fd_data(Fd),
+ get_handle_nif(FRef)
catch
- error:_ -> {error, badarg}
+ error:badarg -> {error, badarg}
end.
+%% Resets the write head to the position the user believes we're at, which may
+%% not be the same as the real one when read caching is in effect.
+reset_write_position(Fd) ->
+ #{ r_buffer := RBuf } = Fd#file_descriptor.data,
+ case prim_buffer:size(RBuf) of
+ Size when Size > 0 -> position(Fd, cur);
+ Size when Size =:= 0 -> ok
+ end.
-%% altname/{1,2}
-
-altname(File) ->
- altname_int({?DRV, [binary]}, File).
-
-altname(Port, File) when is_port(Port) ->
- altname_int(Port, File).
-
-altname_int(Port, File) ->
- drv_command(Port, [?FILE_ALTNAME, pathname(File)],
- fun handle_fname_response/1).
-
-%% write_file_info/{2,3,4}
-
-write_file_info(File, Info) ->
- write_file_info_int({?DRV, [binary]}, File, Info, local).
-
-write_file_info(Port, File, Info) when is_port(Port) ->
- write_file_info_int(Port, File, Info, local);
-write_file_info(File, Info, Opts) ->
- write_file_info_int({?DRV, [binary]}, File, Info, plgv(time, Opts, local)).
-
-write_file_info(Port, File, Info, Opts) when is_port(Port) ->
- write_file_info_int(Port, File, Info, plgv(time, Opts, local)).
+get_fd_data(#file_descriptor{ data = Data }) ->
+ #{ owner := Owner } = Data,
+ case self() of
+ Owner -> Data;
+ _ -> error(not_on_controlling_process)
+ end.
-write_file_info_int(Port, File,
- #file_info{mode=Mode,
- uid=Uid,
- gid=Gid,
- atime=Atime0,
- mtime=Mtime0,
- ctime=Ctime0},
- TimeType) ->
+build_fd_data(FRef, Modes) ->
+ Defaults =
+ #{ owner => self(),
+ handle => FRef,
+ r_ahead_size => 0,
+ r_buffer => prim_buffer:new() },
+ fill_fd_option_map(Modes, Defaults).
+
+fill_fd_option_map([], Map) ->
+ Map;
+
+fill_fd_option_map([read_ahead | Modes], Map) ->
+ fill_fd_option_map([{read_ahead, 64 bsl 10} | Modes], Map);
+fill_fd_option_map([{read_ahead, Size} | Modes], Map) ->
+ fill_fd_option_map(Modes, Map#{ r_ahead_size => Size });
+
+fill_fd_option_map([_Ignored | Modes], Map) ->
+ fill_fd_option_map(Modes, Map).
+
+open_nif(_Name, _Modes) ->
+ erlang:nif_error(undef).
+close_nif(_FileRef) ->
+ erlang:nif_error(undef).
+read_nif(_FileRef, _Size) ->
+ erlang:nif_error(undef).
+write_nif(_FileRef, _IOVec) ->
+ erlang:nif_error(undef).
+pread_nif(_FileRef, _Offset, _Size) ->
+ erlang:nif_error(undef).
+pwrite_nif(_FileRef, _Offset, _IOVec) ->
+ erlang:nif_error(undef).
+seek_nif(_FileRef, _Mark, _Offset) ->
+ erlang:nif_error(undef).
+sync_nif(_FileRef, _DataOnly) ->
+ erlang:nif_error(undef).
+advise_nif(_FileRef, _Offset, _Length, _Advise) ->
+ erlang:nif_error(undef).
+allocate_nif(_FileRef, _Offset, _Length) ->
+ erlang:nif_error(undef).
+truncate_nif(_FileRef) ->
+ erlang:nif_error(undef).
+get_handle_nif(_FileRef) ->
+ erlang:nif_error(undef).
+delayed_close_nif(_FileRef) ->
+ erlang:nif_error(undef).
- % Atime and/or Mtime might be undefined
- % - use localtime() for atime, if atime is undefined
- % - use atime as mtime if mtime is undefined
- % - use mtime as ctime if ctime is undefined
+%%
+%% Quality-of-life helpers
+%%
+read_file(Filename) ->
+ %% We're doing this operation in the NIF to avoid excessive rescheduling.
try
- Atime = file_info_validate_atime(Atime0, TimeType),
- Mtime = file_info_validate_mtime(Mtime0, Atime),
- Ctime = file_info_validate_ctime(Ctime0, Mtime),
-
- drv_command(Port, [?FILE_WRITE_INFO,
- int_to_int32bytes(Mode),
- int_to_int32bytes(Uid),
- int_to_int32bytes(Gid),
- int_to_int64bytes(to_seconds(Atime, TimeType)),
- int_to_int64bytes(to_seconds(Mtime, TimeType)),
- int_to_int64bytes(to_seconds(Ctime, TimeType)),
- pathname(File)])
+ read_file_nif(encode_path(Filename))
catch
- error:_ -> {error, badarg}
+ error:badarg -> {error, badarg}
+ end.
+read_file_nif(_Filename) ->
+ erlang:nif_error(undef).
+
+write_file(Filename, Bytes) ->
+ write_file(Filename, Bytes, []).
+write_file(Filename, Bytes, Modes) ->
+ case open(Filename, [write, binary | Modes]) of
+ {ok, Fd} ->
+ Result = write(Fd, Bytes),
+ close(Fd),
+ Result;
+ {error, Reason} ->
+ {error, Reason}
end.
+%%
+%% Filesystem operations
+%%
-file_info_validate_atime(Atime, _) when Atime =/= undefined -> Atime;
-file_info_validate_atime(undefined, local) -> erlang:localtime();
-file_info_validate_atime(undefined, universal) -> erlang:universaltime();
-file_info_validate_atime(undefined, posix) -> erlang:universaltime_to_posixtime(erlang:universaltime()).
-
-file_info_validate_mtime(undefined, Atime) -> Atime;
-file_info_validate_mtime(Mtime, _) -> Mtime.
-
-file_info_validate_ctime(undefined, Mtime) -> Mtime;
-file_info_validate_ctime(Ctime, _) -> Ctime.
-
-%% make_link/{2,3}
-
-make_link(Old, New) ->
- make_link_int({?DRV, [binary]}, Old, New).
-
-make_link(Port, Old, New) when is_port(Port) ->
- make_link_int(Port, Old, New).
-
-make_link_int(Port, Old, New) ->
- drv_command(Port, [?FILE_LINK, pathname(Old), pathname(New)]).
-
-
-
-%% make_symlink/{2,3}
-
-make_symlink(Old, New) ->
- make_symlink_int({?DRV, [binary]}, Old, New).
-
-make_symlink(Port, Old, New) when is_port(Port) ->
- make_symlink_int(Port, Old, New).
-
-make_symlink_int(Port, Old, New) ->
- drv_command(Port, [?FILE_SYMLINK, pathname(Old), pathname(New)]).
-
-
-
-%% read_link/{2,3}
-
-read_link(Link) ->
- read_link_int({?DRV, [binary]}, Link).
-
-read_link(Port, Link) when is_port(Port) ->
- read_link_int(Port, Link).
-
-read_link_int(Port, Link) ->
- drv_command(Port, [?FILE_READLINK, pathname(Link)],
- fun handle_fname_response/1).
-
-%% read_link_all/{2,3}
-
-read_link_all(Link) ->
- read_link_all_int({?DRV, [binary]}, Link).
-
-read_link_all(Port, Link) when is_port(Port) ->
- read_link_all_int(Port, Link).
-
-read_link_all_int(Port, Link) ->
- drv_command(Port, [?FILE_READLINK, pathname(Link)],
- fun handle_fname_response_all/1).
-
+read_link(Name) -> read_link_1(Name, false).
+read_link_all(Name) -> read_link_1(Name, true).
+read_link_1(Name, AcceptRawNames) ->
+ try read_link_nif(encode_path(Name)) of
+ {ok, RawName} -> translate_raw_name(RawName, AcceptRawNames);
+ {error, Reason} -> {error, Reason}
+ catch
+ error:badarg -> {error, badarg}
+ end.
-%% read_link_info/{2,3}
+translate_raw_name(RawName, SilentFailure) ->
+ case decode_path(RawName) of
+ Converted when is_list(Converted) -> {ok, Converted};
+ {error, _Reason} when SilentFailure =:= false -> {error, einval};
+ {error, _Reason} when SilentFailure =:= true -> {ok, RawName}
+ end.
-read_link_info(Link) ->
- read_link_info_int({?DRV, [binary]}, Link, local).
+list_dir(Name) -> list_dir_1(Name, true).
+list_dir_all(Name) -> list_dir_1(Name, false).
-read_link_info(Port, Link) when is_port(Port) ->
- read_link_info_int(Port, Link, local);
+list_dir_1(Name, SkipInvalid) ->
+ try list_dir_nif(encode_path(Name)) of
+ {ok, RawNames} -> list_dir_convert(RawNames, SkipInvalid, []);
+ {error, Reason} -> {error, Reason}
+ catch
+ error:badarg -> {error, badarg}
+ end.
-read_link_info(Link, Opts) ->
- read_link_info_int({?DRV, [binary]}, Link, plgv(time, Opts, local)).
+list_dir_convert([], _SkipInvalid, Result) ->
+ {ok, Result};
+list_dir_convert([RawName | Rest], SkipInvalid, Result) ->
+ case decode_path(RawName) of
+ Converted when is_list(Converted) ->
+ list_dir_convert(Rest, SkipInvalid, [Converted | Result]);
+ {error, _} when SkipInvalid =:= false ->
+ list_dir_convert(Rest, SkipInvalid, [RawName | Result]);
+
+ %% If the filename cannot be converted, return error or ignore with
+ %% optional error logger warning depending on +fn{u|a}{i|e|w} emulator
+ %% switches.
+ {error, ignore} ->
+ list_dir_convert(Rest, SkipInvalid, Result);
+ {error, warning} ->
+ %% this is equal to calling error_logger:warning_msg/2 which
+ %% we don't want to do from code_server during system boot
+ logger ! {log,warning,"Non-unicode filename ~p ignored\n", [RawName],
+ #{pid=>self(),
+ gl=>group_leader(),
+ time=>erlang:system_time(microsecond),
+ error_logger=>#{tag=>warning_msg}}},
+ list_dir_convert(Rest, SkipInvalid, Result);
+ {error, _} ->
+ {error, {no_translation, RawName}}
+ end.
-read_link_info(Port, Link, Opts) when is_port(Port) ->
- read_link_info_int(Port, Link, plgv(time, Opts, local)).
+read_file_info(Filename) ->
+ read_info_1(Filename, 1, local).
+read_file_info(Filename, Opts) ->
+ read_info_1(Filename, 1, proplist_get_value(time, Opts, local)).
+read_link_info(Name) ->
+ read_info_1(Name, 0, local).
+read_link_info(Name, Opts) ->
+ read_info_1(Name, 0, proplist_get_value(time, Opts, local)).
-read_link_info_int(Port, Link, TimeType) ->
+read_info_1(Name, FollowLinks, TimeType) ->
try
- case drv_command(Port, [?FILE_LSTAT, pathname(Link)]) of
- {ok, FI} -> {ok, FI#file_info{
- ctime = from_seconds(FI#file_info.ctime, TimeType),
- mtime = from_seconds(FI#file_info.mtime, TimeType),
- atime = from_seconds(FI#file_info.atime, TimeType)
- }};
- Error -> Error
- end
+ case read_info_nif(encode_path(Name), FollowLinks) of
+ {error, Reason} ->
+ {error, Reason};
+ FileInfo ->
+ CTime = from_posix_seconds(FileInfo#file_info.ctime, TimeType),
+ MTime = from_posix_seconds(FileInfo#file_info.mtime, TimeType),
+ ATime = from_posix_seconds(FileInfo#file_info.atime, TimeType),
+ {ok, FileInfo#file_info{ ctime = CTime,
+ mtime = MTime,
+ atime = ATime }}
+ end
catch
- error:_ -> {error, badarg}
+ error:_ -> {error, badarg}
end.
-%% list_dir/{1,2}
-
-list_dir(Dir) ->
- list_dir_int({?DRV, [binary]}, Dir).
-
-list_dir(Port, Dir) when is_port(Port) ->
- list_dir_int(Port, Dir).
-
-list_dir_int(Port, Dir) ->
- drv_command(Port, [?FILE_READDIR, pathname(Dir)],
- fun(P) ->
- case list_dir_response(P, []) of
- {ok, RawNames} ->
- try
- {ok, list_dir_convert(RawNames)}
- catch
- throw:Reason ->
- Reason
- end;
- Error ->
- Error
- end
- end).
-
-list_dir_all(Dir) ->
- list_dir_all_int({?DRV, [binary]}, Dir).
-
-list_dir_all(Port, Dir) when is_port(Port) ->
- list_dir_all_int(Port, Dir).
-
-list_dir_all_int(Port, Dir) ->
- drv_command(Port, [?FILE_READDIR, pathname(Dir)],
- fun(P) ->
- case list_dir_response(P, []) of
- {ok, RawNames} ->
- {ok, list_dir_convert_all(RawNames)};
- Error ->
- Error
- end
- end).
-
-list_dir_response(Port, Acc0) ->
- case drv_get_response(Port) of
- {lfname, []} ->
- {ok, Acc0};
- {lfname, Names} ->
- Acc = [Name || <<L:16,Name:L/binary>> <= Names] ++ Acc0,
- list_dir_response(Port, Acc);
- Error ->
- Error
+write_file_info(Filename, Info) ->
+ write_file_info_1(Filename, Info, local).
+write_file_info(Filename, Info, Opts) ->
+ write_file_info_1(Filename, Info, proplist_get_value(time, Opts, local)).
+
+write_file_info_1(Filename, Info, TimeType) ->
+ #file_info{ mode = Modes,
+ uid = Uid,
+ gid = Gid,
+ atime = ATime0,
+ mtime = MTime0,
+ ctime = CTime0} = Info,
+ try
+ % ATime and/or MTime might be undefined
+ % - use localtime() for atime, if atime is undefined
+ % - use atime as mtime if mtime is undefined
+ % - use mtime as ctime if ctime is undefined
+ ATime = file_info_convert_atime(ATime0, TimeType),
+ MTime = file_info_convert_mtime(MTime0, ATime, TimeType),
+ CTime = file_info_convert_ctime(CTime0, MTime, TimeType),
+ EncodedName = encode_path(Filename),
+
+ %% This is a bit ugly but we need to handle partial failures the same
+ %% way the old driver did.
+ throw_on_error(set_owner(EncodedName, Uid, Gid)),
+ throw_on_error(set_permissions(EncodedName, Modes)),
+ throw_on_error(set_time(EncodedName, ATime, MTime, CTime))
+ catch
+ throw:Reason -> {error, Reason};
+ error:_ -> {error, badarg}
end.
-list_dir_convert([Name|Names]) ->
- %% If the filename cannot be converted, return error or ignore
- %% with optional error logger warning, depending on +fn{u|a}{i|e|w}
- %% emulator switches.
- case prim_file:internal_native2name(Name) of
- {error, warning} ->
- error_logger:warning_msg("Non-unicode filename ~p ignored\n",
- [Name]),
- list_dir_convert(Names);
- {error, ignore} ->
- list_dir_convert(Names);
- {error, error} ->
- throw({error, {no_translation, Name}});
- Converted when is_list(Converted) ->
- [Converted|list_dir_convert(Names)]
- end;
-list_dir_convert([]) -> [].
-
-list_dir_convert_all([Name|Names]) ->
- %% If the filename cannot be converted, retain the filename as
- %% a binary.
- case prim_file:internal_native2name(Name) of
- {error, _} ->
- [Name|list_dir_convert_all(Names)];
- Converted when is_list(Converted) ->
- [Converted|list_dir_convert_all(Names)]
- end;
-list_dir_convert_all([]) -> [].
-
-%%%-----------------------------------------------------------------
-%%% Functions to communicate with the driver
-
-handle_fname_response(Port) ->
- case drv_get_response(Port) of
- {fname, Name} ->
- case prim_file:internal_native2name(Name) of
- {error, warning} ->
- error_logger:warning_msg("Non-unicode filename ~p "
- "ignored when reading link\n",
- [Name]),
- {error, einval};
- {error, _} ->
- {error, einval};
- Converted when is_list(Converted) ->
- {ok, Converted}
- end;
- Error ->
- Error
- end.
+set_owner(EncodedName, Uid, undefined) ->
+ set_owner(EncodedName, Uid, -1);
+set_owner(EncodedName, undefined, Gid) ->
+ set_owner(EncodedName, -1, Gid);
+set_owner(EncodedName, Uid, Gid) ->
+ set_owner_nif(EncodedName, Uid, Gid).
+set_owner_nif(_Path, _Uid, _Gid) ->
+ erlang:nif_error(undef).
-handle_fname_response_all(Port) ->
- case drv_get_response(Port) of
- {fname, Name} ->
- case prim_file:internal_native2name(Name) of
- {error, _} ->
- {ok, Name};
- Converted when is_list(Converted) ->
- {ok, Converted}
- end;
- Error ->
- Error
+set_permissions(_EncodedName, undefined) ->
+ ok;
+set_permissions(EncodedName, Permissions) ->
+ set_permissions_nif(EncodedName, Permissions).
+set_permissions_nif(_Path, _Permissions) ->
+ erlang:nif_error(undef).
+
+set_time(EncodedName, ATime, MTime, CTime) ->
+ set_time_nif(EncodedName, ATime, MTime, CTime).
+set_time_nif(_Path, _ATime, _MTime, _CTime) ->
+ erlang:nif_error(undef).
+
+throw_on_error(ok) -> ok;
+throw_on_error({error, enotsup}) -> ok;
+throw_on_error({error, Reason}) -> throw(Reason).
+
+file_info_convert_atime(ATime, TimeType) when ATime =/= undefined ->
+ to_posix_seconds(ATime, TimeType);
+file_info_convert_atime(undefined, local) ->
+ to_posix_seconds(erlang:localtime(), local);
+file_info_convert_atime(undefined, universal) ->
+ to_posix_seconds(erlang:universaltime(), universal);
+file_info_convert_atime(undefined, posix) ->
+ erlang:universaltime_to_posixtime(erlang:universaltime()).
+
+file_info_convert_mtime(undefined, ATime, _TimeType) ->
+ ATime;
+file_info_convert_mtime(MTime, _ATime, TimeType) ->
+ to_posix_seconds(MTime, TimeType).
+
+file_info_convert_ctime(undefined, MTime, _TimeType) ->
+ MTime;
+file_info_convert_ctime(CTime, _MTime, TimeType) ->
+ to_posix_seconds(CTime, TimeType).
+
+%% This is only relevant on Windows, so we assume that format to simplify the
+%% internals.
+get_cwd([Letter, $:]) when Letter >= $A, Letter =< $Z ->
+ get_dcwd(Letter - $A + 1);
+get_cwd([Letter, $:]) when Letter >= $a, Letter =< $z ->
+ get_dcwd(Letter - $a + 1);
+get_cwd([_|_]) ->
+ {error, einval};
+get_cwd(_) ->
+ {error, badarg}.
+get_dcwd(Index) ->
+ try get_device_cwd_nif(Index) of
+ {ok, RawPath} -> {ok, decode_path(RawPath)};
+ {error, Reason} -> {error, Reason}
+ catch
+ error:badarg -> {error, badarg}
end.
-%% Opens a driver port and converts any problems into {error, emfile}.
-%% Returns {ok, Port} when successful.
-
-drv_open(Driver, Portopts) ->
- try erlang:open_port({spawn_driver, Driver}, Portopts) of
- Port ->
- {ok, Port}
+get_cwd() ->
+ try get_cwd_nif() of
+ {ok, RawPath} -> {ok, decode_path(RawPath)};
+ {error, Reason} -> {error, Reason}
catch
- error:Reason ->
- {error, Reason}
+ error:badarg -> {error, badarg}
end.
-
-
-
-%% Closes a port in a safe way. Returns ok.
-
-drv_close(Port) ->
- Save = erlang:dt_spread_tag(false),
+set_cwd(Path) ->
try
- try erlang:port_close(Port) catch error:_ -> ok end,
- receive %% Ugly workaround in case the caller==owner traps exits
- {'EXIT', Port, _Reason} ->
- ok
- after 0 ->
- ok
- end
- after
- erlang:dt_restore_tag(Save)
+ case is_path_translatable(Path) of
+ true -> set_cwd_nif(encode_path(Path));
+ false -> {error, no_translation}
+ end
+ catch
+ error:badarg -> {error, badarg}
end.
-
-
-%% Issues a command to a port and gets the response.
-%% If Port is {Driver, Portopts} a port is first opened and
-%% then closed after the result has been received.
-%% Returns {ok, Result} or {error, Reason}.
-
-drv_command(Port, Command) ->
- drv_command(Port, Command, undefined).
-
-drv_command(Port, Command, R) when is_binary(Command) ->
- drv_command(Port, Command, true, R);
-drv_command(Port, Command, R) ->
- try erlang:iolist_size(Command) of
- _ ->
- drv_command(Port, Command, true, R)
+delete(Path) ->
+ try
+ del_file_nif(encode_path(Path))
catch
- error:Reason ->
- {error, Reason}
+ error:badarg -> {error, badarg}
end.
-drv_command(Port, Command, Validated, R) when is_port(Port) ->
- Save = erlang:dt_spread_tag(false),
- try erlang:port_command(Port, erlang:dt_append_vm_tag_data(Command)) of
- true ->
- drv_get_response(Port, R)
+rename(Source, Destination) ->
+ try
+ rename_nif(encode_path(Source), encode_path(Destination))
catch
- %% If the Command is valid, knowing that the port is a port,
- %% a badarg error must mean it is a dead port, that is:
- %% a currently invalid filehandle, -> einval, not badarg.
- error:badarg when Validated ->
- {error, einval};
- error:badarg ->
- try erlang:iolist_size(Command) of
- _ -> % Valid
- {error, einval}
- catch
- error:_ ->
- {error, badarg}
- end;
- error:Reason ->
- {error, Reason}
- after
- erlang:dt_restore_tag(Save)
- end;
-drv_command({Driver, Portopts}, Command, Validated, R) ->
- case drv_open(Driver, Portopts) of
- {ok, Port} ->
- Result = drv_command(Port, Command, Validated, R),
- drv_close(Port),
- Result;
- Error ->
- Error
+ error:badarg -> {error, badarg}
end.
-drv_command_nt(Port, Command, R) when is_port(Port) ->
- Save = erlang:dt_spread_tag(false),
- try erlang:port_command(Port, Command) of
- true ->
- drv_get_response(Port, R)
+make_dir(Path) ->
+ try
+ make_dir_nif(encode_path(Path))
catch
- error:badarg ->
- try erlang:iolist_size(Command) of
- _ -> % Valid
- {error, einval}
- catch
- error:_ ->
- {error, badarg}
- end;
- error:Reason ->
- {error, Reason}
- after
- erlang:dt_restore_tag(Save)
+ error:badarg -> {error, badarg}
end.
-
-
-
-%% Receives the response from a driver port.
-%% Returns: {ok, ListOrBinary}|{error, Reason}
-
-drv_get_response(Port, undefined) ->
- drv_get_response(Port);
-drv_get_response(Port, Fun) when is_function(Fun, 1) ->
- Fun(Port).
-
-drv_get_response(Port) ->
- erlang:bump_reductions(100),
- receive
- {Port, {data, [Response|Rest] = Data}} ->
- try translate_response(Response, Rest)
- catch
- error:Reason ->
- {error, {bad_response_from_port, Data,
- {Reason, erlang:get_stacktrace()}}}
- end;
- {'EXIT', Port, Reason} ->
- {error, {port_died, Reason}}
+del_dir(Path) ->
+ try
+ del_dir_nif(encode_path(Path))
+ catch
+ error:badarg -> {error, badarg}
end.
-
-
-%%%-----------------------------------------------------------------
-%%% Utility functions.
-
-%% Converts a list of mode atoms into a mode word for the driver.
-%% Returns {Mode, Portopts, Setopts} where Portopts is a list of
-%% options for erlang:open_port/2 and Setopts is a list of
-%% setopt commands to send to the port, or error Reason upon failure.
-
-open_mode(List) when is_list(List) ->
- case open_mode(List, 0, [], []) of
- {Mode, Portopts, Setopts} when Mode band
- (?EFILE_MODE_READ bor ?EFILE_MODE_WRITE)
- =:= 0 ->
- {Mode bor ?EFILE_MODE_READ, Portopts, Setopts};
- Other ->
- Other
+make_link(Existing, New) ->
+ try
+ make_hard_link_nif(encode_path(Existing), encode_path(New))
+ catch
+ error:badarg -> {error, badarg}
end.
-
-open_mode([raw|Rest], Mode, Portopts, Setopts) ->
- open_mode(Rest, Mode, Portopts, Setopts);
-open_mode([read|Rest], Mode, Portopts, Setopts) ->
- open_mode(Rest, Mode bor ?EFILE_MODE_READ, Portopts, Setopts);
-open_mode([write|Rest], Mode, Portopts, Setopts) ->
- open_mode(Rest, Mode bor ?EFILE_MODE_WRITE, Portopts, Setopts);
-open_mode([binary|Rest], Mode, Portopts, Setopts) ->
- open_mode(Rest, Mode, [binary | Portopts], Setopts);
-open_mode([compressed|Rest], Mode, Portopts, Setopts) ->
- open_mode(Rest, Mode bor ?EFILE_COMPRESSED, Portopts, Setopts);
-open_mode([append|Rest], Mode, Portopts, Setopts) ->
- open_mode(Rest, Mode bor ?EFILE_MODE_APPEND bor ?EFILE_MODE_WRITE,
- Portopts, Setopts);
-open_mode([exclusive|Rest], Mode, Portopts, Setopts) ->
- open_mode(Rest, Mode bor ?EFILE_MODE_EXCL, Portopts, Setopts);
-open_mode([sync|Rest], Mode, Portopts, Setopts) ->
- open_mode(Rest, Mode bor ?EFILE_MODE_SYNC, Portopts, Setopts);
-open_mode([delayed_write|Rest], Mode, Portopts, Setopts) ->
- open_mode([{delayed_write, 64*1024, 2000}|Rest], Mode,
- Portopts, Setopts);
-open_mode([{delayed_write, Size, Delay}|Rest], Mode, Portopts, Setopts)
- when is_integer(Size), 0 =< Size, is_integer(Delay), 0 =< Delay ->
- if
- Size < ?LARGEFILESIZE, Delay < 1 bsl 64 ->
- open_mode(Rest, Mode, Portopts,
- [<<?FILE_SETOPT, ?FILE_OPT_DELAYED_WRITE,
- Size:64, Delay:64>>
- | Setopts]);
- true ->
- einval
- end;
-open_mode([read_ahead|Rest], Mode, Portopts, Setopts) ->
- open_mode([{read_ahead, 64*1024}|Rest], Mode, Portopts, Setopts);
-open_mode([{read_ahead, Size}|Rest], Mode, Portopts, Setopts)
- when is_integer(Size), 0 =< Size ->
- if
- Size < ?LARGEFILESIZE ->
- open_mode(Rest, Mode, Portopts,
- [<<?FILE_SETOPT, ?FILE_OPT_READ_AHEAD,
- Size:64>> | Setopts]);
- true ->
- einval
- end;
-open_mode([], Mode, Portopts, Setopts) ->
- {Mode, reverse(Portopts), reverse(Setopts)};
-open_mode(_, _Mode, _Portopts, _Setopts) ->
- badarg.
-
-
-
-%% Converts a position tuple {bof, X} | {cur, X} | {eof, X} into
-%% {Offset, OriginCode} for the driver.
-%% Returns badarg upon failure.
-
-lseek_position(Pos)
- when is_integer(Pos) ->
- lseek_position({bof, Pos});
-lseek_position(bof) ->
- lseek_position({bof, 0});
-lseek_position(cur) ->
- lseek_position({cur, 0});
-lseek_position(eof) ->
- lseek_position({eof, 0});
-lseek_position({bof, Offset})
- when is_integer(Offset) ->
- {Offset, ?EFILE_SEEK_SET};
-lseek_position({cur, Offset})
- when is_integer(Offset) ->
- {Offset, ?EFILE_SEEK_CUR};
-lseek_position({eof, Offset})
- when is_integer(Offset) ->
- {Offset, ?EFILE_SEEK_END};
-lseek_position(_) ->
- badarg.
-
-
-
-%% Translates the response from the driver into
-%% {ok, Result} or {error, Reason}.
-
--dialyzer({no_improper_lists, translate_response/2}).
-translate_response(?FILE_RESP_OK, []) ->
- ok;
-translate_response(?FILE_RESP_ERROR, List) when is_list(List) ->
- {error, list_to_atom(List)};
-translate_response(?FILE_RESP_NUMBER, List) ->
- {N, []} = get_uint64(List),
- {ok, N};
-translate_response(?FILE_RESP_DATA, List) ->
- {_N, _Data} = ND = get_uint64(List),
- {ok, ND};
-translate_response(?FILE_RESP_INFO, List) when is_list(List) ->
- {ok, transform_info(List)};
-translate_response(?FILE_RESP_NUMERR, L0) ->
- {N, L1} = get_uint64(L0),
- {error, {N, list_to_atom(L1)}};
-translate_response(?FILE_RESP_LDATA, List) ->
- {ok, transform_ldata(List)};
-translate_response(?FILE_RESP_N2DATA,
- <<Offset:64, 0:64, Size:64>>) ->
- {ok, {Size, Offset, eof}};
-translate_response(?FILE_RESP_N2DATA,
- [<<Offset:64, 0:64, Size:64>> | <<>>]) ->
- {ok, {Size, Offset, eof}};
-translate_response(?FILE_RESP_N2DATA = X,
- [<<_:64, 0:64, _:64>> | _] = Data) ->
- {error, {bad_response_from_port, [X | Data]}};
-translate_response(?FILE_RESP_N2DATA = X,
- [<<_:64, _:64, _:64>> | <<>>] = Data) ->
- {error, {bad_response_from_port, [X | Data]}};
-translate_response(?FILE_RESP_N2DATA,
- [<<Offset:64, _ReadSize:64, Size:64>> | D]) ->
- {ok, {Size, Offset, D}};
-translate_response(?FILE_RESP_N2DATA = X, L0) when is_list(L0) ->
- {Offset, L1} = get_uint64(L0),
- {ReadSize, L2} = get_uint64(L1),
- {Size, L3} = get_uint64(L2),
- case {ReadSize, L3} of
- {0, []} ->
- {ok, {Size, Offset, eof}};
- {0, _} ->
- {error, {bad_response_from_port, [X | L0]}};
- {_, []} ->
- {error, {bad_response_from_port, [X | L0]}};
- _ ->
- {ok, {Size, Offset, L3}}
- end;
-translate_response(?FILE_RESP_EOF, []) ->
- eof;
-translate_response(?FILE_RESP_FNAME, Data) ->
- {fname, Data};
-translate_response(?FILE_RESP_LFNAME, Data) ->
- {lfname, Data};
-translate_response(?FILE_RESP_ALL_DATA, Data) ->
- {ok, Data};
-translate_response(X, Data) ->
- {error, {bad_response_from_port, [X | Data]}}.
-
-transform_info([
- Hsize1, Hsize2, Hsize3, Hsize4,
- Lsize1, Lsize2, Lsize3, Lsize4,
- Type1, Type2, Type3, Type4,
- Atime1, Atime2, Atime3, Atime4, Atime5, Atime6, Atime7, Atime8,
- Mtime1, Mtime2, Mtime3, Mtime4, Mtime5, Mtime6, Mtime7, Mtime8,
- Ctime1, Ctime2, Ctime3, Ctime4, Ctime5, Ctime6, Ctime7, Ctime8,
- Mode1, Mode2, Mode3, Mode4,
- Links1, Links2, Links3, Links4,
- Major1, Major2, Major3, Major4,
- Minor1, Minor2, Minor3, Minor4,
- Inode1, Inode2, Inode3, Inode4,
- Uid1, Uid2, Uid3, Uid4,
- Gid1, Gid2, Gid3, Gid4,
- Access1,Access2,Access3,Access4]) ->
- #file_info {
- size = uint32(Hsize1,Hsize2,Hsize3,Hsize4)*16#100000000 + uint32(Lsize1,Lsize2,Lsize3,Lsize4),
- type = file_type(uint32(Type1,Type2,Type3,Type4)),
- access = file_access(uint32(Access1,Access2,Access3,Access4)),
- atime = sint64(Atime1, Atime2, Atime3, Atime4, Atime5, Atime6, Atime7, Atime8),
- mtime = sint64(Mtime1, Mtime2, Mtime3, Mtime4, Mtime5, Mtime6, Mtime7, Mtime8),
- ctime = sint64(Ctime1, Ctime2, Ctime3, Ctime4, Ctime5, Ctime6, Ctime7, Ctime8),
- mode = uint32(Mode1,Mode2,Mode3,Mode4),
- links = uint32(Links1,Links2,Links3,Links4),
- major_device = uint32(Major1,Major2,Major3,Major4),
- minor_device = uint32(Minor1,Minor2,Minor3,Minor4),
- inode = uint32(Inode1,Inode2,Inode3,Inode4),
- uid = uint32(Uid1,Uid2,Uid3,Uid4),
- gid = uint32(Gid1,Gid2,Gid3,Gid4)
- }.
-
-
-file_type(1) -> device;
-file_type(2) -> directory;
-file_type(3) -> regular;
-file_type(4) -> symlink;
-file_type(_) -> other.
-
-file_access(0) -> none;
-file_access(1) -> write;
-file_access(2) -> read;
-file_access(3) -> read_write.
-
-int_to_int32bytes(Int) when is_integer(Int) ->
- <<Int:32>>;
-int_to_int32bytes(undefined) ->
- <<-1:32>>.
-
-int_to_int64bytes(Int) when is_integer(Int) ->
- <<Int:64/signed>>.
-
-
-sint64(I1,I2,I3,I4,I5,I6,I7,I8) when I1 > 127 ->
- ((I1 bsl 56) bor (I2 bsl 48) bor (I3 bsl 40) bor (I4 bsl 32) bor
- (I5 bsl 24) bor (I6 bsl 16) bor (I7 bsl 8) bor I8) - (1 bsl 64);
-sint64(I1,I2,I3,I4,I5,I6,I7,I8) ->
- ((I1 bsl 56) bor (I2 bsl 48) bor (I3 bsl 40) bor (I4 bsl 32) bor
- (I5 bsl 24) bor (I6 bsl 16) bor (I7 bsl 8) bor I8).
-
-
-uint32(X1,X2,X3,X4) ->
- (X1 bsl 24) bor (X2 bsl 16) bor (X3 bsl 8) bor X4.
-
-get_uint64(L0) ->
- {X1, L1} = get_uint32(L0),
- {X2, L2} = get_uint32(L1),
- {(X1 bsl 32) bor X2, L2}.
-
-get_uint32([X1,X2,X3,X4|List]) ->
- {(((((X1 bsl 8) bor X2) bsl 8) bor X3) bsl 8) bor X4, List}.
-
-
-%% Binary mode
-transform_ldata(<<0:32, 0:32>>) ->
- [];
-transform_ldata([<<0:32, N:32, Sizes/binary>> | Datas]) ->
- transform_ldata(N, Sizes, Datas, []);
-%% List mode
-transform_ldata([_,_,_,_,_,_,_,_|_] = L0) ->
- {0, L1} = get_uint32(L0),
- {N, L2} = get_uint32(L1),
- transform_ldata(N, L2, []).
-
-%% List mode
-transform_ldata(0, List, Sizes) ->
- transform_ldata(0, List, reverse(Sizes), []);
-transform_ldata(N, L0, Sizes) ->
- {Size, L1} = get_uint64(L0),
- transform_ldata(N-1, L1, [Size | Sizes]).
-
-%% Binary mode
-transform_ldata(1, <<0:64>>, <<>>, R) ->
- reverse(R, [eof]);
-transform_ldata(1, <<Size:64>>, Data, R)
- when byte_size(Data) =:= Size ->
- reverse(R, [Data]);
-transform_ldata(N, <<0:64, Sizes/binary>>, [<<>> | Datas], R) ->
- transform_ldata(N-1, Sizes, Datas, [eof | R]);
-transform_ldata(N, <<Size:64, Sizes/binary>>, [Data | Datas], R)
- when byte_size(Data) =:= Size ->
- transform_ldata(N-1, Sizes, Datas, [Data | R]);
-%% List mode
-transform_ldata(0, [], [], R) ->
- reverse(R);
-transform_ldata(0, List, [0 | Sizes], R) ->
- transform_ldata(0, List, Sizes, [eof | R]);
-transform_ldata(0, List, [Size | Sizes], R) ->
- {Front, Rear} = lists_split(List, Size),
- transform_ldata(0, Rear, Sizes, [Front | R]).
-
-lists_split(List, 0) when is_list(List) ->
- {[], List};
-lists_split(List, N) when is_list(List), is_integer(N), N < 0 ->
- erlang:error(badarg, [List, N]);
-lists_split(List, N) when is_list(List), is_integer(N) ->
- case lists_split(List, N, []) of
- premature_end_of_list ->
- erlang:error(badarg, [List, N]);
- Result ->
- Result
+make_symlink(Existing, New) ->
+ try
+ make_soft_link_nif(encode_path(Existing), encode_path(New))
+ catch
+ error:badarg -> {error, badarg}
end.
-lists_split(List, 0, Rev) ->
- {reverse(Rev), List};
-lists_split([], _, _) ->
- premature_end_of_list;
-lists_split([Hd | Tl], N, Rev) ->
- lists_split(Tl, N-1, [Hd | Rev]).
-
-%% We KNOW that lists:reverse/2 is a BIF.
-
-reverse(X) -> lists:reverse(X, []).
-reverse(L, T) -> lists:reverse(L, T).
+altname(Path) ->
+ try altname_nif(encode_path(Path)) of
+ {ok, RawPath} -> {ok, decode_path(RawPath)};
+ Other -> Other
+ catch
+ error:badarg -> {error, badarg}
+ end.
-% Will add zero termination too
-% The 'EXIT' tuple from a bad argument will eventually generate an error
-% in list_to_binary, which is caught and generates the {error,badarg} return
-pathname(File) ->
- (catch prim_file:internal_name2native(File)).
+list_dir_nif(_Path) ->
+ erlang:nif_error(undef).
+read_link_nif(_Path) ->
+ erlang:nif_error(undef).
+read_info_nif(_Path, _FollowLinks) ->
+ erlang:nif_error(undef).
+make_hard_link_nif(_Existing, _New) ->
+ erlang:nif_error(undef).
+make_soft_link_nif(_Existing, _New) ->
+ erlang:nif_error(undef).
+rename_nif(_Source, _Destination) ->
+ erlang:nif_error(undef).
+make_dir_nif(_Path) ->
+ erlang:nif_error(undef).
+del_file_nif(_Path) ->
+ erlang:nif_error(undef).
+del_dir_nif(_Path) ->
+ erlang:nif_error(undef).
+get_device_cwd_nif(_DevicePath) ->
+ erlang:nif_error(undef).
+set_cwd_nif(_Path) ->
+ erlang:nif_error(undef).
+get_cwd_nif() ->
+ erlang:nif_error(undef).
+altname_nif(_Path) ->
+ erlang:nif_error(undef).
+%%
+%% General helper functions.
+%%
-%% proplist:get_value/3
-plgv(K, [{K, V}|_], _) -> V;
-plgv(K, [_|KVs], D) -> plgv(K, KVs, D);
-plgv(_, [], D) -> D.
+%% We know for certain that lists:reverse/2 is a BIF, so it's safe to use it
+%% even though this module is preloaded.
+reverse_list(List) -> lists:reverse(List, []).
+
+proplist_get_value(_Key, [], Default) ->
+ Default;
+proplist_get_value(Key, [{Key, Value} | _Rest], _Default) ->
+ Value;
+proplist_get_value(Key, [Key | _Rest], _Default) ->
+ true;
+proplist_get_value(Key, [_Other | Rest], Default) ->
+ proplist_get_value(Key, Rest, Default).
+
+encode_path(Path) ->
+ prim_file:internal_name2native(Path).
+decode_path(NativePath) when is_binary(NativePath) ->
+ prim_file:internal_native2name(NativePath).
+
+is_path_translatable(Path) when is_list(Path) ->
+ true;
+is_path_translatable(Path) ->
+ prim_file:is_translatable(Path).
-%%
%% We don't actually want this here
+%%
%% We want to use posix time in all prim but erl_prim_loader makes that tricky
%% It is probably needed to redo the whole erl_prim_loader
-from_seconds(Seconds, posix) when is_integer(Seconds) ->
+from_posix_seconds(Seconds, posix) when is_integer(Seconds) ->
Seconds;
-from_seconds(Seconds, universal) when is_integer(Seconds) ->
+from_posix_seconds(Seconds, universal) when is_integer(Seconds) ->
erlang:posixtime_to_universaltime(Seconds);
-from_seconds(Seconds, local) when is_integer(Seconds) ->
+from_posix_seconds(Seconds, local) when is_integer(Seconds) ->
erlang:universaltime_to_localtime(erlang:posixtime_to_universaltime(Seconds)).
-to_seconds(Seconds, posix) when is_integer(Seconds) ->
+to_posix_seconds(Seconds, posix) when is_integer(Seconds) ->
Seconds;
-to_seconds({_,_} = Datetime, universal) ->
+to_posix_seconds({_,_} = Datetime, universal) ->
erlang:universaltime_to_posixtime(Datetime);
-to_seconds({_,_} = Datetime, local) ->
+to_posix_seconds({_,_} = Datetime, local) ->
erlang:universaltime_to_posixtime(erlang:localtime_to_universaltime(Datetime)).