-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 %% Return true if calling `stat' on `Path' was successful. %% @see can_stat/2 %% @end %% ------------------------------------------------------------------------------ -spec can_stat(file:filename()) -> boolean(). can_stat(Path) -> can_stat(Path, 2500). %%------------------------------------------------------------------------------ %% @doc %% Return true if calling `stat' on `Path' was successful. %% %% This function will not cause `file' or `filelib' operations to block, in case %% the underlying `stat' operation blocks. This is in contrast to `file' and %% `filelib' behaviour, where `filelib:is_regular/1' on a path that is in a %% stale NFS mount will cause other `filelib:...' operations on totally %% unrelated paths to block, too. %% %% Also, subsequent invocations 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', %% see {@link info/1}. %% %% @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, 2500). %%------------------------------------------------------------------------------ %% @doc %% Check that `RootDirectory++"/"++RelPath' is a regular file. %% Return `false' if filesystem errors or timeouts were encountered. %% %% The non-functional behaviour regarding safe access to directories is the same %% as in {@link can_stat/2}. %% %% @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() -> lists:foreach( fun safeio_directory_guard:info/1, 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.