Packages

Check for stale (NFS) files/directories without locking up all file IO.

Current section

Files

Jump to
safeio src safeio.erl
Raw

src/safeio.erl

-module(safeio).
%% API exports
-export([can_stat/1,
can_stat/2,
is_regular/2,
is_regular/3,
info/0,
info/1
]).
-export_type([ioerror/0, filetype/0]).
-type ioerror() ::
permission_denied
| io_error
| not_found
| bad_path
| other_error.
-type filetype() ::
regular_file
| directory
| other_filetype.
%%====================================================================
%% API functions
%%====================================================================
%%------------------------------------------------------------------------------
%% @doc
%% Check that `Path` is an accessable path, e.g. a directory to a network
%% mounted file system, that might be blocked due to network problems.
%% @see can_state/2
%% @end
%% ------------------------------------------------------------------------------
-spec can_stat(file:filename()) -> boolean().
can_stat(Path) ->
can_stat(Path, 5000).
%%------------------------------------------------------------------------------
%% @doc
%% Check that `Path` is an accessable path, e.g. a directory to a network
%% mounted file system, that might be blocked due to network problems.
%%
%% If the `Path` is _safe_ execute the MFA in the calling process.
%%
%% This function will not clog the file I/O of other `file` or `filelib`
%% operations in case the underlying `stat` operation blocks.
%%
%% For example, if an NFS mount is located at "/mnt/nfs" and a file
%% "/mnt/nfs/user-provided/a76f7e62" is expected to be a regular file then
%% `safeio:is_regular("/mnt/nfs", "user-provided/a76f7e62")` will be a safe way
%% to determine if the file is a regular file and even if the NFS mount is
%% stale, this function returns after a short timeout.
%%
%% Also, subsequent calls to `check` will immediately return `false` if a check
%% for `Path`, that was initiated by previous invocation, has not returned yet.
%% This mechanism works by starting an internal server for each distinct `Path`.
%%
%% @end
%% ------------------------------------------------------------------------------
-spec can_stat(file:filename(), timeout()) -> boolean().
can_stat(Path, TimeOutMillis) ->
case safeio_sup:get_directory_guard(Path) of
{ok, Port} ->
case safeio_directory_guard:can_stat(Port, TimeOutMillis) of
ok ->
true;
_ ->
false
end;
_ ->
false
end.
%%------------------------------------------------------------------------------
%% @doc
%% Check that `RootDirectory++"/"++RelativePath` is a regular file.
%% Return `false` if filesystem errors or timeouts were encountered.
%% @see is_regular/3
%% @end
%%------------------------------------------------------------------------------
-spec is_regular(file:filename(), file:filename()) -> boolean().
is_regular(RootDirectory, RelativePath) ->
is_regular(RootDirectory, RelativePath, 5000).
%%------------------------------------------------------------------------------
%% @doc
%% Check that `RootDirectory++"/"++RelPath` is a re file.
%% Return `false` if filesystem errors or timeouts were encountered.
%%
%% In contrast to {@link filelib:is_regular/1} this function will not
%% clog the file I/O of other `file` or `filelib` operations in case
%% the underlying `stat` operation blocks.
%%
%% For example, if an NFS mount is located at "/mnt/nfs" and a file
%% "/mnt/nfs/user-provided/a76f7e62" is expected to be a regular file
%% then `safeio:is_regular("/mnt/nfs", "user-provided/a76f7e62")`
%% will be a safe way to determine if the file is a regular file and
%% even if the NFS mount is stale, this function returns after a short
%% timeout.
%%
%% Also, subsequent calls to the `is regular` will immediately return
%% `false` if a check for "/mnt/nfs", that was initiated by previous
%% invokation, has not returned yet.
%% This mechanism works by starting an internal server for each distinct
%% `RootDirectory`.
%% @end
%%------------------------------------------------------------------------------
-spec is_regular(file:filename(), file:filename(), timeout()) -> boolean().
is_regular(RootDir, RelPath, TimeOut) ->
case safeio_sup:get_directory_guard(RootDir) of
{ok, Port} ->
case safeio_directory_guard:get_filetype(Port, RelPath, TimeOut) of
regular_file ->
true;
_ ->
false
end;
_ ->
false
end.
%%------------------------------------------------------------------------------
%% @private
%%------------------------------------------------------------------------------
-spec info() -> ok.
info() ->
[safeio_directory_guard:info(G) || G <- safeio_sup:which_directory_guards()].
%%------------------------------------------------------------------------------
%% @private
%%------------------------------------------------------------------------------
-spec info(file:filename()) -> ok.
info(RootDir) ->
case safeio_sup:get_directory_guard(RootDir) of
{ok, Port} ->
safeio_directory_guard:info(Port);
_ ->
ok
end.
%%====================================================================
%% Internal functions
%%====================================================================