From 420ebf84936b7642f7539bae7904ff779f7cae9a Mon Sep 17 00:00:00 2001 From: Yurii Rashkovskii Date: Sun, 10 Jul 2011 07:49:38 -0700 Subject: [PATCH] Implemented evfs_io_server and first bits of evfs_iolit_io_server --- src/evfs_io_server.erl | 112 ++++++++++++++++++++++++++++++++++ src/evfs_iolist_io_server.erl | 99 ++++++++++++++++++++++++++++++ test/evfs_test_fs.erl | 10 ++- test/evfs_tests.erl | 8 ++- 4 files changed, 226 insertions(+), 3 deletions(-) create mode 100644 src/evfs_io_server.erl create mode 100644 src/evfs_iolist_io_server.erl diff --git a/src/evfs_io_server.erl b/src/evfs_io_server.erl new file mode 100644 index 0000000..8c509f3 --- /dev/null +++ b/src/evfs_io_server.erl @@ -0,0 +1,112 @@ +-module(evfs_io_server). +-behaviour(gen_server). +-export([behaviour_info/1]). + +-export([start_link/2]). +%% gen_server callbacks +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, + terminate/2, code_change/3]). + +behaviour_info(callbacks) -> + []; +behaviour_info(_) -> + undefined. + +-record(state, + { + module :: module(), + handle :: term() + }). + +-type state() :: #state{}. + +-spec start_link(module(), term()) -> {ok, pid()} | {error, term()}. + +start_link(Module, Args) -> + gen_server:start_link(?MODULE, {Module, Args}, []). + +-spec init(module()) -> {ok, state()} | {stop, term()}. + +init({Module, Args}) -> + {ok, #state{ module = Module, handle = Args }}. + +-spec handle_call(term(), term(), state()) -> + {noreply, state()} | + {stop, normal, stopped, state()}. +handle_call(_Request, _From, State) -> + {noreply, State}. + +-spec handle_cast(term(), state()) -> {noreply, state()}. + +handle_cast(_Msg, State) -> + {noreply, State}. + +-spec handle_info(term(), state()) -> + {noreply, state()} | {stop, term(), state()}. + +handle_info({file_request, From, ReplyAs, Request}, + #state{ module = Module, handle = Handle } = State) when is_pid(From) -> + case file_request(Request, Module, Handle) of + {reply, Reply, Handle1} -> + From ! {file_reply, ReplyAs, Reply}, + {noreply, State#state{ handle = Handle1} }; + {error, Reply, Handle1} -> + From ! {file_reply, ReplyAs, Reply}, + {noreply, State#state{ handle = Handle1} }; + {stop, Reason, Reply, Handle1} -> + From ! {file_reply, ReplyAs, Reply}, + {stop, Reason, State#state{ handle = Handle1 }} + end; + +handle_info({io_request, From, ReplyAs, Request}, + #state{ module = Module, handle = Handle} = State) when is_pid(From) -> + case io_request(Request, Module, Handle) of + {reply, Reply, Handle1} -> + From ! {io_reply, ReplyAs, Reply}, + {noreply, State#state{ handle = Handle1} }; + {error, Reply, Handle1} -> + From ! {io_reply, ReplyAs, Reply}, + {noreply, State#state{ handle = Handle1} }; + {stop, Reason, Reply, Handle1} -> + From ! {io_reply, ReplyAs, Reply}, + {stop, Reason, State#state{ handle = Handle1 }} + end. + + +-spec terminate(term(), state()) -> ok. + +terminate(_Reason, _State) -> + ok. + +-spec code_change(term(), state(), term()) -> {ok, state()}. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +%% Private +file_request(Request, Module, Handle) -> + Command = element(1, Request), + Args = tl(tuple_to_list(Request)), + case (catch apply(Module, Command, Args ++ [Handle])) of + {'EXIT', {undef, _}} -> + file_request(Request, evfs_iolist_io_server, Module:io_list(Handle)); + Result -> + Result + end. + +io_request({requests, Requests}, Module, Handle) -> + io_request({requests, Requests, undefined}, Module, Handle); +io_request({requests, [], Result}, _Module, _Handle) -> + Result; +io_request({requests, [Request|Rest], _Result}, Module, Handle) -> + Result = io_request(Request, Module, Handle), + io_request({requests, Rest, Result}, Module, Handle); +io_request(Request, Module, Handle) -> + Command = element(1, Request), + Args = tl(tuple_to_list(Request)), + case (catch apply(Module, Command, Args ++ [Handle])) of + {'EXIT', {undef, _}} -> + file_request(Request, evfs_iolist_io_server, Module:io_list(Handle)); + Result -> + Result + end. diff --git a/src/evfs_iolist_io_server.erl b/src/evfs_iolist_io_server.erl new file mode 100644 index 0000000..826c735 --- /dev/null +++ b/src/evfs_iolist_io_server.erl @@ -0,0 +1,99 @@ +-module(evfs_iolist_io_server). +-behaviour(evfs_io_server). +%% File requests +-export([ + advise/4, + pread/3, + pwrite/3, + datasync/1, + sync/1, + close/1, + position/1, + truncate/1 + ]). +%% IO requests +-export([ + put_chars/2, + put_chars/3, + put_chars/4, + put_chars/5, + get_until/4, + get_until/5, + get_until/6, + get_chars/3, + get_chars/4, + get_line/2, + get_line/3, + setopts/2, + getopts/1 + ]). + +%% File requests +advise(_Offset, _Length, _Advise, Handle) -> + {reply, ok, Handle}. + +pread(At, Sz, Handle) -> + Binary = iolist_to_binary(Handle), + {reply, {ok, binary_to_list(binary:part(Binary, {At, Sz}))}, Handle}. + +pwrite(_At, _Data, Handle) -> + %% TODO + {reply, ok, Handle}. + +datasync(Handle) -> + {reply, ok, Handle}. + +sync(Handle) -> + {reply, ok, Handle}. + +close(Handle) -> + {stop, normal, ok, Handle}. + +position(Handle) -> + {reply, 0, Handle}. + +truncate(Handle) -> + {reply, ok, Handle}. + +%% IO requests +put_chars(Chars, Handle) -> + put_chars(latin1, Chars, Handle). + +put_chars(_Enc, _Mod, _Func, _Args, Handle) -> + {reply, {error, notsup}, Handle}. + +put_chars(Mod, Func, Args, Handle) -> + put_chars(latin1, Mod, Func, Args, Handle). + + +put_chars(_Enc, _Chars, Handle) -> + {reply, {error, notsup}, Handle}. + + +get_until(_Enc, _Prompt, _Mod, _Func, _XtraArgs, Handle) -> + {reply, {error, notsup}, Handle}. + +get_until(_Enc, _Prompt, _N, Handle) -> + {reply, {error, notsup}, Handle}. + +get_until(Prompt, Mod, Func, XtraArgs, Handle) -> + get_until(latin1, Prompt, Mod, Func, XtraArgs, Handle). + +get_chars(_Enc, _Prompt, _N, Handle) -> + {reply, {error, notsup}, Handle}. + +get_chars(Prompt, N, Handle) -> + get_chars(latin1, Prompt, N, Handle). + +get_line(_Enc, _Prompt, Handle) -> + {reply, {error, notsup}, Handle}. + +get_line(Prompt, Handle) -> + get_line(latin1, Prompt, Handle). + +setopts(_Opts, Handle) -> + {reply, {error, notsup}, Handle}. + +getopts(Handle) -> + {reply, {error, notsup}, Handle}. + diff --git a/test/evfs_test_fs.erl b/test/evfs_test_fs.erl index 12d1827..779f06a 100644 --- a/test/evfs_test_fs.erl +++ b/test/evfs_test_fs.erl @@ -1,5 +1,6 @@ -module(evfs_test_fs). -behaviour(evfs_handler). +-behaviour(evfs_io_server). -export([init/1, supports/2, terminate/2]). -export([open/3, read_file/2, @@ -21,6 +22,7 @@ make_symlink/3, copy/6 ]). +-export([io_list/1]). init(Args) -> {ok, Args}. @@ -32,8 +34,9 @@ supports(_, State) -> %% Filesystem API -open(_Filename, _Mode, State) -> - {ok, {error, enotsup}, State}. +open(Filename, _Mode, State) -> + Child = evfs_io_server:start_link(?MODULE, Filename), + {ok, Child, State}. read_file(Filename, State) -> {ok, {ok, list_to_binary(Filename)}, State}. @@ -92,3 +95,6 @@ copy(_SourceName, _SourceOpts, _DestName, _DestOpts, _Length, State) -> terminate(_Reason, _State) -> ok. +%% IO server +io_list(Handle) -> + Handle. diff --git a/test/evfs_tests.erl b/test/evfs_tests.erl index 075a49e..9a3cc44 100644 --- a/test/evfs_tests.erl +++ b/test/evfs_tests.erl @@ -17,6 +17,11 @@ t_read_file() -> ?assertEqual({error, eisdir}, file:read_file("/")), ?assertEqual({ok, <<"test://any">>}, file:read_file("test://any")). +t_pread() -> + evfs:register(evfs_test_fs, []), + {ok, File} = file:open("test://anyfile",[read]), + ?assertEqual({ok, ["test://any"]}, file:pread(File, [{0,10}])). + evfs_test_() -> {foreach, fun () -> @@ -28,7 +33,8 @@ evfs_test_() -> [ {"unregister", ?_test(t_unregister())}, {"file:// scheme should route to the default file server", ?_test(t_file_scheme())}, - {"test_fs read_file", ?_test(t_read_file())} + {"test_fs read_file", ?_test(t_read_file())}, + {"test_fs pread", ?_test(t_pread())} ] }.