diff --git a/rebar.config b/rebar.config --- a/rebar.config +++ b/rebar.config @@ -8,14 +8,19 @@ {hex, [{doc, ex_doc}]}. {project_plugins, [ - rebar3_ex_doc, - rebar3_proper, - covertool - ]}. + rebar3_ex_doc, + rebar3_proper, + covertool, + erlfmt +]}. + +{erlfmt, [write]}. -{profiles, [{test, [ - {deps, [proper]}, - {erl_opts, [nowarn_export_all]} - ]}]}. +{profiles, [ + {test, [ + {deps, [proper]}, + {erl_opts, [nowarn_export_all]} + ]} +]}. {ct_opts, [{create_priv_dir, auto_per_run}]}. diff --git a/src/e9p.app.src b/src/e9p.app.src --- a/src/e9p.app.src +++ b/src/e9p.app.src @@ -1,20 +1,20 @@ % SPDX-FileCopyrightText: 2025 Ɓukasz Niemier <~@hauleth.dev> % % SPDX-License-Identifier: Apache-2.0 -{application, e9p, - [{description, "Implementation of 9p2000 protocol in Erlang"}, - {vsn, git}, - {registered, []}, - {applications, - [kernel, - stdlib - ]}, - {env,[]}, - {modules, []}, +{application, e9p, [ + {description, "Implementation of 9p2000 protocol in Erlang"}, + {vsn, git}, + {registered, []}, + {applications, [ + kernel, + stdlib + ]}, + {env, []}, + {modules, []}, - {licenses, ["Apache-2.0"]}, - {links, [ - {"Tangled", "https://tangled.org/hauleth.dev/e9p"}, - {"intro(5)", "https://man.cat-v.org/plan_9/5/intro"} - ]} - ]}. + {licenses, ["Apache-2.0"]}, + {links, [ + {"Tangled", "https://tangled.org/hauleth.dev/e9p"}, + {"intro(5)", "https://man.cat-v.org/plan_9/5/intro"} + ]} +]}. diff --git a/src/e9p.erl b/src/e9p.erl --- a/src/e9p.erl +++ b/src/e9p.erl @@ -12,7 +12,7 @@ -export_type([u8/0, u16/0, u32/0, u64/0]). -include("e9p_internal.hrl"). --type u8() :: 16#00..16#FF. +-type u8() :: 16#00..16#FF. -type u16() :: 16#0000..16#FFFF. -type u32() :: 16#00000000..16#FFFFFFFF. -type u64() :: 16#0000000000000000..16#FFFFFFFFFFFFFFFF. @@ -29,10 +29,10 @@ is_type(#qid{type = QType}, Type) -> (to_qtype(Type) band QType) =/= 0. to_qtype(directory) -> 16#80; -to_qtype(append) -> 16#40; -to_qtype(excl) -> 16#20; -to_qtype(device) -> 16#10; -to_qtype(auth) -> 16#08; -to_qtype(tmp) -> 16#04; -to_qtype(symlink) -> 16#02; -to_qtype(regular) -> 16#00. +to_qtype(append) -> 16#40; +to_qtype(excl) -> 16#20; +to_qtype(device) -> 16#10; +to_qtype(auth) -> 16#08; +to_qtype(tmp) -> 16#04; +to_qtype(symlink) -> 16#02; +to_qtype(regular) -> 16#00. diff --git a/src/e9p_client.erl b/src/e9p_client.erl --- a/src/e9p_client.erl +++ b/src/e9p_client.erl @@ -24,14 +24,15 @@ {ok, Socket} = gen_tcp:connect(Host, Port, [{active, false}, binary]), case version_negotiation(Socket) of {ok, #rversion{max_packet_size = MaxPacketSize, version = ?version}} -> inet:setopts(Socket, [{active, once}]), - {ok, - #{socket => Socket, - buffer => <<>>, - tag => 0, - fid => 0, - msgs => #{}, - max_packet_size => MaxPacketSize, - version => ?version}}; + {ok, #{ + socket => Socket, + buffer => <<>>, + tag => 0, + fid => 0, + msgs => #{}, + max_packet_size => MaxPacketSize, + version => ?version + }}; {ok, #rversion{version = OtherVersion}} -> {error, {unsupported_version, OtherVersion}}; {error, _} = Error -> @@ -39,10 +40,12 @@ Error end. handle_call({attach, Auth, Uname, Aname}, From, State) -> - #{tag := Tag, - fid := Fid, - socket := Socket, - msgs := Msgs} = + #{ + tag := Tag, + fid := Fid, + socket := Socket, + msgs := Msgs + } = State, Afid = case Auth of @@ -51,15 +54,18 @@ ?nofid; Id -> Id end, - Msg = #tattach{fid = Fid, - afid = Afid, - uname = Uname, - aname = Aname}, + Msg = #tattach{ + fid = Fid, + afid = Afid, + uname = Uname, + aname = Aname + }, e9p_transport:send(Socket, Tag, Msg), - {noreply, - State#{tag := Tag + 1, - fid := Fid + 1, - msgs := Msgs#{Tag => {From, #{fid => Fid}}}}}; + {noreply, State#{ + tag := Tag + 1, + fid := Fid + 1, + msgs := Msgs#{Tag => {From, #{fid => Fid}}} + }}; handle_call(_Msg, _From, State) -> {reply, {error, not_implemented}, State}. diff --git a/src/e9p_fs.erl b/src/e9p_fs.erl --- a/src/e9p_fs.erl +++ b/src/e9p_fs.erl @@ -7,18 +7,18 @@ %% @end -module(e9p_fs). -export([ - init/1, - root/3, - walk/3, - open/3, - create/5, - read/4, - write/4, - clunk/2, - remove/2, - stat/2, - wstat/3 - ]). + init/1, + root/3, + walk/3, + open/3, + create/5, + read/4, + write/4, + clunk/2, + remove/2, + stat/2, + wstat/3 +]). -include("e9p_internal.hrl"). -include_lib("kernel/include/logger.hrl"). @@ -33,21 +33,23 @@ -type result() :: {ok, state()} | {error, term(), state()}. -type result(T) :: {ok, T, state()} | {error, term(), state()}. -define(if_supported(Code), - case erlang:function_exported(Mod, ?FUNCTION_NAME, ?FUNCTION_ARITY) of - true -> - case (fun() -> Code end)() of - {ok, Ret, NewState} -> - {ok, Ret, {Mod, NewState}}; - {error, Error, NewState} -> - {error, Error, {Mod, NewState}} - end; - false -> {error, nosupport, {Mod, State}} - end). + case erlang:function_exported(Mod, ?FUNCTION_NAME, ?FUNCTION_ARITY) of + true -> + case (fun() -> Code end)() of + {ok, Ret, NewState} -> + {ok, Ret, {Mod, NewState}}; + {error, Error, NewState} -> + {error, Error, {Mod, NewState}} + end; + false -> + {error, nosupport, {Mod, State}} + end +). %% Setup state for given filesystem. -callback init(term()) -> - {ok, state()} | - {error, Reason :: term()}. + {ok, state()} + | {error, Reason :: term()}. %% Returns `QID' for root node. %% @@ -65,26 +67,32 @@ {fid() | false, state()}. -callback open(fid(), path(), Mode :: integer(), state()) -> result({fid_state(), e9p:u32()}). --callback create(fid(), - path(), - Name :: unicode:chardata(), - Perm :: e9p:u32(), - Mode :: e9p:u8(), - state()) -> result({fid(), IOUnit :: e9p:u32()}). +-callback create( + fid(), + path(), + Name :: unicode:chardata(), + Perm :: e9p:u32(), + Mode :: e9p:u8(), + state() +) -> result({fid(), IOUnit :: e9p:u32()}). %% Read data from file indicated by `QID' --callback read(fid(), - path(), - Offset :: non_neg_integer(), - Length :: non_neg_integer(), - state()) -> result({fid_state(), iodata()}). +-callback read( + fid(), + path(), + Offset :: non_neg_integer(), + Length :: non_neg_integer(), + state() +) -> result({fid_state(), iodata()}). %% Write data to file indicated by `QID' --callback write(fid(), - path(), - Offset :: non_neg_integer(), - Data :: iodata(), - state()) -> result({fid_state(), non_neg_integer()}). +-callback write( + fid(), + path(), + Offset :: non_neg_integer(), + Data :: iodata(), + state() +) -> result({fid_state(), non_neg_integer()}). -callback clunk(fid(), path(), state()) -> result(). @@ -97,9 +105,9 @@ %% Write stat data for file indicated by `QID' -callback wstat(fid(), path(), map(), state()) -> result(). -optional_callbacks([ - flush/1, - clunk/3 - ]). + flush/1, + clunk/3 +]). init({Mod, State}) -> case Mod:init(State) of @@ -150,15 +158,20 @@ EMode = translate_mode(Mode), case Mod:open({QID, FState0}, Path, EMode, State0) of {ok, {FState, IOUnit}, State} -> {ok, {FID#fid{state = FState}, IOUnit}, {Mod, State}}; - {error, Reason, StateE} -> {error, Reason, {Mod, StateE}} + {error, Reason, StateE} -> + {error, Reason, {Mod, StateE}} end. translate_mode(Mode) when Mode >= 16#10 -> [trunc | translate_mode(Mode band 16#EF)]; -translate_mode(0) -> [read]; -translate_mode(1) -> [write]; -translate_mode(2) -> [append]; -translate_mode(3) -> [exec]. +translate_mode(0) -> + [read]; +translate_mode(1) -> + [write]; +translate_mode(2) -> + [append]; +translate_mode(3) -> + [exec]. create({Mod, State}, #fid{qid = QID, path = Path, state = FState}, Name, Perm, Mode) -> ?if_supported(Mod:create({QID, FState}, Path, Name, Perm, Mode, State)). @@ -182,7 +195,8 @@ case Mod:clunk({QID, FState}, Path, State0) of {ok, State} -> {ok, {Mod, State}}; {error, Reason, StateE} -> {error, Reason, {Mod, StateE}} end; - false -> {ok, {Mod, State0}} + false -> + {ok, {Mod, State0}} end. remove({Mod, State0}, #fid{qid = QID, path = Path, state = FState}) -> diff --git a/src/e9p_internal.hrl b/src/e9p_internal.hrl --- a/src/e9p_internal.hrl +++ b/src/e9p_internal.hrl @@ -9,8 +9,8 @@ -define(nofid, 16#FFFFFFFF). -define(max_packet_size, 8168). --define(int, little-unsigned-unit:8). --define(len, 2/?int). +-define(int, little - unsigned - unit:8). +-define(len, 2 / ?int). -define(Tversion, 100). -define(Rversion, 101). diff --git a/src/e9p_msg.erl b/src/e9p_msg.erl --- a/src/e9p_msg.erl +++ b/src/e9p_msg.erl @@ -8,45 +8,46 @@ -module(e9p_msg). -export([parse/1, encode/2, encode_stat/1, parse_stat/1]). --export_type([tag/0, - message/0, - request_message/0, - response_message/0 - ]). +-export_type([ + tag/0, + message/0, + request_message/0, + response_message/0 +]). -include("e9p_internal.hrl"). -type tag() :: 16#0000..16#FFFF. -type request_message() :: - #tversion{} | - #tauth{} | - #tattach{} | - #tflush{} | - #twalk{} | - #topen{} | - #tcreate{} | - #tread{} | - #twrite{} | - #tclunk{} | - #tremove{} | - #tstat{} | - #twstat{}. + #tversion{} + | #tauth{} + | #tattach{} + | #tflush{} + | #twalk{} + | #topen{} + | #tcreate{} + | #tread{} + | #twrite{} + | #tclunk{} + | #tremove{} + | #tstat{} + | #twstat{}. -type response_message() :: - #rversion{} | - #rauth{} | - #rattach{} | - #rerror{} | - #rflush{} | - #rwalk{} | - #ropen{} | - #rcreate{} | - #rread{} | - #rwrite{} | - #rclunk{} | - #rstat{} | - #rwstat{}. + #rversion{} + | #rauth{} + | #rattach{} + | #rerror{} + | #rflush{} + | #rwalk{} + | #ropen{} + | #rcreate{} + | #rread{} + | #rwrite{} + | #rclunk{} + | #rstat{} + | #rwstat{}. -type message() :: request_message() | response_message(). @@ -64,64 +65,57 @@ do_parse(?Tversion, <>) -> {ok, #tversion{max_packet_size = MSize, version = Version}}; do_parse(?Rversion, <>) -> {ok, #rversion{max_packet_size = MSize, version = Version}}; - %% attach, auth - messages to establish a connection -do_parse(?Tauth, <>) -> - {ok, #tauth{afid = AFID, - uname = Uname, - aname = Aname}}; +do_parse( + ?Tauth, + <> +) -> + {ok, #tauth{ + afid = AFID, + uname = Uname, + aname = Aname + }}; do_parse(?Rauth, <>) -> {ok, #rauth{aqid = binary_to_qid(AQID)}}; - -do_parse(?Tattach, <>) -> - {ok, #tattach{fid = FID, - afid = AFID, - uname = Uname, - aname = Aname}}; +do_parse( + ?Tattach, + <> +) -> + {ok, #tattach{ + fid = FID, + afid = AFID, + uname = Uname, + aname = Aname + }}; do_parse(?Rattach, <>) -> {ok, #rattach{qid = binary_to_qid(QID)}}; - %% clunk - forget about a fid do_parse(?Tclunk, <>) -> {ok, #tclunk{fid = FID}}; do_parse(?Rclunk, <<>>) -> {ok, #rclunk{}}; - %% error - return an error do_parse(?Rerror, <>) -> {ok, #rerror{msg = Error}}; - %% flush - abort a message do_parse(?Tflush, <>) -> {ok, #tflush{tag = Tag}}; do_parse(?Rflush, <<>>) -> {ok, #rflush{}}; - %% open, create - prepare a fid for I/O on an existing or new file do_parse(?Topen, <>) -> {ok, #topen{fid = FID, mode = Mode}}; do_parse(?Ropen, <>) -> {ok, #ropen{qid = binary_to_qid(QID), io_unit = IOUnit}}; - -do_parse(?Tcreate, <>) -> +do_parse(?Tcreate, <>) -> {ok, #tcreate{fid = FID, name = Name, perm = Perm, mode = Mode}}; do_parse(?Rcreate, <>) -> {ok, #rcreate{qid = binary_to_qid(QID), io_unit = IOUnit}}; - %% remove - remove a file from a server do_parse(?Tremove, <>) -> {ok, #tremove{fid = FID}}; do_parse(?Rremove, <<>>) -> {ok, #rremove{}}; - %% stat, wstat - inquire or change file attributes do_parse(?Tstat, <>) -> {ok, #tstat{fid = FID}}; @@ -129,22 +123,18 @@ do_parse(?Rstat, <>) -> case parse_stat(Data) of {ok, Stat} -> {ok, #rstat{stat = Stat}}; - {error, _} = Error -> Error end; - do_parse(?Twstat, <>) -> case parse_stat(Data) of {ok, Stat} -> {ok, #twstat{fid = FID, stat = Stat}}; - {error, _} = Error -> Error end; do_parse(?Rwstat, <<>>) -> {ok, #rwstat{}}; - %% walk - descend a directory hierarchy do_parse(?Twalk, <>) -> NWNames = [Name || <> <= Rest], @@ -157,7 +147,6 @@ {error, {invalid_walk_length, NWNLen, Len}} end; do_parse(?Rwalk, <>) -> {ok, #rwalk{qids = [binary_to_qid(QID) || <> <= QIDs]}}; - do_parse(?Tread, <>) -> {ok, #tread{fid = FID, offset = Offset, len = Len}}; do_parse(?Rread, <>) -> @@ -166,106 +155,88 @@ do_parse(?Twrite, <>) -> {ok, #twrite{fid = FID, offset = Offset, data = Data}}; do_parse(?Rwrite, <>) -> {ok, #rwrite{len = Len}}; - do_parse(Type, Data) -> {error, {invalid_message, Type, Data}}. -parse_stat(<<_Size:2/?int, - Type:2/?int, - Dev:4/?int, - RawQID:13/binary, - Mode:4/?int, - Atime:4/?int, - Mtime:4/?int, - Len:8/?int, - NLen:?len, Name:NLen/binary, - ULen:?len, Uid:ULen/binary, - GLen:?len, Gid:GLen/binary, - MULen:?len, MUid:MULen/binary>>) --> +parse_stat( + <<_Size:2/?int, Type:2/?int, Dev:4/?int, RawQID:13/binary, Mode:4/?int, Atime:4/?int, + Mtime:4/?int, Len:8/?int, NLen:?len, Name:NLen/binary, ULen:?len, Uid:ULen/binary, + GLen:?len, Gid:GLen/binary, MULen:?len, MUid:MULen/binary>> +) -> QID = binary_to_qid(RawQID), Flags = qid_to_mode_flags(QID), {ok, #{ - type => Type, - dev => Dev, - qid => QID, - mode => Mode band (bnot Flags), - atime => Atime, - mtime => Mtime, - length => Len, - name => Name, - uid => Uid, - gid => Gid, - muid => MUid - }}; -parse_stat(_) -> {error, invalid_stat_data}. + type => Type, + dev => Dev, + qid => QID, + mode => Mode band (bnot Flags), + atime => Atime, + mtime => Mtime, + length => Len, + name => Name, + uid => Uid, + gid => Gid, + muid => MUid + }}; +parse_stat(_) -> + {error, invalid_stat_data}. -spec encode(Tag :: tag() | notag, Data :: message()) -> iodata(). encode(Tag, Data) -> {MT, Encoded} = do_encode(Data), - Tag0 = case Tag of - notag -> ?notag; - V -> V - end, + Tag0 = + case Tag of + notag -> ?notag; + V -> V + end, [<> | Encoded]. do_encode(#tversion{max_packet_size = MSize, version = Version}) -> {?Tversion, [<> | encode_str(Version)]}; do_encode(#rversion{max_packet_size = MSize, version = Version}) -> {?Rversion, [<> | encode_str(Version)]}; - do_encode(#tauth{afid = AFID, uname = Uname, aname = Aname}) -> {?Tauth, [<>, encode_str(Uname), encode_str(Aname)]}; do_encode(#rauth{aqid = AQID}) -> {?Rauth, qid_to_binary(AQID)}; - do_encode(#tattach{fid = FID, afid = AFID, uname = Uname, aname = Aname}) -> {?Tattach, [<>, encode_str(Uname), encode_str(Aname)]}; do_encode(#rattach{qid = QID}) -> {?Rattach, qid_to_binary(QID)}; - do_encode(#tclunk{fid = FID}) -> {?Tclunk, <>}; do_encode(#rclunk{}) -> {?Rclunk, []}; - do_encode(#rerror{msg = Error}) -> {?Rerror, encode_str(Error)}; - do_encode(#tflush{tag = Tag}) -> {?Tflush, <>}; do_encode(#rflush{}) -> {?Rflush, []}; - do_encode(#topen{fid = FID, mode = Mode}) -> {?Topen, <>}; do_encode(#ropen{qid = QID, io_unit = IOUnit}) -> {?Ropen, [qid_to_binary(QID), <>]}; - do_encode(#tcreate{fid = FID, name = Name, perm = Perm, mode = Mode}) -> {?Tcreate, [<>, encode_str(Name), <>]}; do_encode(#rcreate{qid = QID, io_unit = IOUnit}) -> {?Rcreate, [qid_to_binary(QID), <>]}; - do_encode(#tremove{fid = FID}) -> {?Tremove, <>}; do_encode(#rremove{}) -> {?Rremove, []}; - do_encode(#tstat{fid = FID}) -> {?Tstat, <>}; do_encode(#rstat{stat = Stat}) -> Encoded = encode_stat(Stat), Len = iolist_size(Encoded), {?Rstat, [<> | encode_stat(Stat)]}; - do_encode(#twstat{fid = FID, stat = Stat}) -> Encoded = encode_stat(Stat), Len = iolist_size(Encoded), {?Twstat, [<> | encode_stat(Stat)]}; do_encode(#rwstat{}) -> {?Rwstat, []}; - do_encode(#twalk{fid = FID, new_fid = NewFID, names = Names}) -> ENames = [encode_str(Name) || Name <- Names], Len = length(ENames), @@ -274,7 +245,6 @@ do_encode(#rwalk{qids = QIDs}) -> EQIDs = [qid_to_binary(QID) || QID <- QIDs], Len = length(EQIDs), {?Rwalk, [<> | EQIDs]}; - do_encode(#tread{fid = FID, offset = Offset, len = Len}) -> {?Tread, <>}; do_encode(#rread{data = Data}) -> @@ -288,43 +258,46 @@ {?Rwrite, [<>]}. encode_stat(Stat) -> #{ - type := Type, - dev := Dev, - qid := QID, - mode := Mode, - atime := Atime, - mtime := Mtime, - length := Len, - name := Name, - uid := Uid, - gid := Gid, - muid := MUid - } = maps:merge( - #{ - type => 0, - dev => 0, - mode => 0, - atime => 0, - mtime => 0, - uid => ~"", - gid => ~"", - muid => ~"" - }, Stat), + type := Type, + dev := Dev, + qid := QID, + mode := Mode, + atime := Atime, + mtime := Mtime, + length := Len, + name := Name, + uid := Uid, + gid := Gid, + muid := MUid + } = maps:merge( + #{ + type => 0, + dev => 0, + mode => 0, + atime => 0, + mtime => 0, + uid => ~"", + gid => ~"", + muid => ~"" + }, + Stat + ), FullMode = qid_to_mode_flags(QID) bor Mode, - Encoded = [<< - Type:2/?int, - Dev:4/?int - >>, - qid_to_binary(QID), - <>, - time_to_encoded_sec(Atime), - time_to_encoded_sec(Mtime), - <>, - encode_str(Name), - encode_str(Uid), - encode_str(Gid), - encode_str(MUid) - ], + Encoded = [ + << + Type:2/?int, + Dev:4/?int + >>, + qid_to_binary(QID), + <>, + time_to_encoded_sec(Atime), + time_to_encoded_sec(Mtime), + <>, + encode_str(Name), + encode_str(Uid), + encode_str(Gid), + encode_str(MUid) + ], ELen = iolist_size(Encoded), [<> | Encoded]. diff --git a/src/e9p_server.erl b/src/e9p_server.erl --- a/src/e9p_server.erl +++ b/src/e9p_server.erl @@ -8,21 +8,24 @@ -include("e9p_internal.hrl"). -include_lib("kernel/include/logger.hrl"). --export([start/2, - start_link/2]). +-export([ + start/2, + start_link/2 +]). --export([setup_acceptor/3, - accept_loop/2, - loop/1 - ]). +-export([ + setup_acceptor/3, + accept_loop/2, + loop/1 +]). -record(state, { - socket, - % trans_mod = gen_tcp, - % ver, - fids = #{}, - handler - }). + socket, + % trans_mod = gen_tcp, + % ver, + fids = #{}, + handler +}). start(Port, Handler) -> proc_lib:start(?MODULE, setup_acceptor, [self(), Port, Handler]). @@ -34,9 +37,7 @@ setup_acceptor(Parent, Port, Handler0) -> maybe {ok, LSock} ?= gen_tcp:listen(Port, [binary, {active, false}]), {ok, Handler} ?= e9p_fs:init(Handler0), - proc_lib:init_ack(Parent, {ok, self()}), - ?MODULE:accept_loop(LSock, Handler) else {error, _} = Error -> @@ -49,9 +50,9 @@ {ok, Sock} -> % TODO: Handle connected clients in separate process State = #state{ - socket = Sock, - handler = Handler - }, + socket = Sock, + handler = Handler + }, ok = ?MODULE:loop(State), ?MODULE:accept_loop(LSock, Handler); {error, timeout} -> @@ -74,11 +75,13 @@ ?MODULE:loop(State#state{handler = RHandler}) catch C:E:S -> ?LOG_ERROR(#{ - kind => C, - msg => E, - stacktrace => S - }), - e9p_transport:send(Sock, Tag, #rerror{msg = io_lib:format("Caught ~p: ~p", [C, E])}), + kind => C, + msg => E, + stacktrace => S + }), + e9p_transport:send(Sock, Tag, #rerror{ + msg = io_lib:format("Caught ~p: ~p", [C, E]) + }), ?MODULE:loop(State) end; {error, closed} -> @@ -90,26 +93,22 @@ handle_message(#tversion{version = <<"9P2000", _/binary>>, max_packet_size = MPS}, FIDs, Handler) -> % Currently only "basic" 9p2000 version is supported, without any extensions % like `.u` or `.L` {ok, #rversion{version = ~"9P2000", max_packet_size = MPS}, FIDs, Handler}; - handle_message(#tflush{}, FIDs, Handler) -> % Currently there is no support for parallel messages, so this does simply % nothing {ok, #rflush{}, FIDs, Handler}; - handle_message(#tattach{fid = FID, uname = UName, aname = AName}, FIDs, Handler0) -> maybe {ok, QID, Handler} ?= e9p_fs:root(Handler0, UName, AName), NFIDs = FIDs#{FID => QID}, {ok, #rattach{qid = QID#fid.qid}, NFIDs, Handler} end; - handle_message(#twalk{fid = FID, new_fid = NewFID, names = Paths}, FIDs, Handler0) -> maybe {ok, QID} ?= get_qid(FIDs, FID), {ok, {NewQID, QIDs}, Handler} ?= e9p_fs:walk(Handler0, QID, Paths), {ok, #rwalk{qids = QIDs}, FIDs#{NewFID => NewQID}, Handler} end; - handle_message(#topen{fid = FID, mode = Mode}, FIDs, Handler0) -> maybe {ok, QID} ?= get_qid(FIDs, FID), @@ -122,7 +121,6 @@ {ok, QID} ?= get_qid(FIDs, FID), {ok, {NewQID, IOUnit}, Handler} ?= e9p_fs:create(Handler0, QID, Name, Perm, Mode), {ok, #rcreate{qid = NewQID#fid.qid, io_unit = IOUnit}, FIDs, Handler} end; - handle_message(#tread{fid = FID, offset = Offset, len = Len}, FIDs, Handler0) -> maybe {ok, QID} ?= get_qid(FIDs, FID), @@ -135,7 +133,6 @@ {ok, QID} ?= get_qid(FIDs, FID), {ok, {NQID, Len}, Handler} ?= e9p_fs:write(Handler0, QID, Offset, Data), {ok, #rwrite{len = Len}, FIDs#{FID => NQID}, Handler} end; - handle_message(#tclunk{fid = FID}, FIDs, Handler0) -> maybe {ok, QID} ?= get_qid(FIDs, FID), @@ -143,28 +140,24 @@ {ok, Handler} ?= e9p_fs:clunk(Handler0, QID), NFIDs = maps:remove(FID, FIDs), {ok, #rclunk{}, NFIDs, Handler} end; - handle_message(#tremove{fid = FID}, FIDs, Handler0) -> maybe {ok, QID} ?= get_qid(FIDs, FID), {ok, Handler} ?= e9p_fs:remove(Handler0, QID), {ok, #rremove{}, FIDs, Handler} end; - handle_message(#tstat{fid = FID}, FIDs, Handler0) -> maybe {ok, QID} ?= get_qid(FIDs, FID), {ok, Stat, Handler} ?= e9p_fs:stat(Handler0, QID), {ok, #rstat{stat = Stat}, FIDs, Handler} end; - handle_message(#twstat{fid = FID, stat = Stat}, FIDs, Handler0) -> maybe {ok, QID} ?= get_qid(FIDs, FID), {ok, Handler} ?= e9p_fs:wstat(Handler0, QID, Stat), {ok, #rwstat{}, FIDs, Handler} end; - handle_message(_Msg, _FIDs, Handler) -> {error, ~"Unknown request type", Handler}. diff --git a/src/e9p_sysfs.erl b/src/e9p_sysfs.erl --- a/src/e9p_sysfs.erl +++ b/src/e9p_sysfs.erl @@ -8,16 +8,18 @@ -behaviour(e9p_fs). -include_lib("kernel/include/logger.hrl"). --export([init/1, - root/3, - walk/4, - open/4, - create/6, - read/5, - write/5, - remove/3, - stat/3, - wstat/4]). +-export([ + init/1, + root/3, + walk/4, + open/4, + create/6, + read/5, + write/5, + remove/3, + stat/3, + wstat/4 +]). init(_State) -> {ok, []}. @@ -38,86 +40,86 @@ open(_FID, [], _Mode, State) -> {ok, {[~"applications", ~"processes", ~"system_info"], 0}, State}; open(_FID, [~"system_info"], _Mode, State) -> Keys = [ - ~"allocated_areas", - % ~"allocator", %% TBD - ~"alloc_util_allocators", - % ~"allocator_sizes", %% TBD - ~"cpu_topology", - ~"logical_processors", - ~"logical_processors_available", - ~"logical_processors_online", - ~"cpu_quota", - ~"update_cpu_info", - ~"fullsweep_after", - ~"garbage_collection", - ~"heap_sizes", - ~"heap_type", - ~"max_heap_size", - ~"message_queue_data", - ~"min_heap_size", - ~"min_bin_vheap_size", - ~"procs", - ~"atom_count", - ~"atom_limit", - ~"ets_count", - ~"ets_limit", - ~"port_count", - ~"port_limit", - ~"process_count", - ~"process_limit", - ~"end_time", - ~"os_monotonic_time_source", - ~"os_system_time_source", - ~"start_time", - ~"time_correction", - ~"time_offset", - ~"time_warp_mode", - ~"tolerant_timeofday", - ~"dirty_cpu_schedulers", - ~"dirty_cpu_schedulers_online", - ~"dirty_io_schedulers", - ~"multi_scheduling", - ~"multi_scheduling_blockers", - ~"normal_multi_scheduling_blockers", - ~"scheduler_bind_type", - ~"scheduler_bindings", - % ~"scheduler_id", %% Intentionally omitted - ~"schedulers", - ~"schedulers_online", - ~"smp_support", - ~"threads", - ~"thread_pool_size", - ~"async_dist", - ~"creation", - ~"delayed_node_table_gc", - ~"dist", - ~"dist_buf_busy_limit", - ~"dist_ctrl", - ~"c_compiler_used", - ~"check_io", - ~"debug_compiled", - ~"driver_version", - ~"dynamic_trace", - ~"dynamic_trace_probes", - ~"emu_flavor", - ~"emu_type", - ~"halt_flush_timeout", - ~"info", - ~"kernel_poll", - ~"loaded", - ~"machine", - ~"modified_timing_level", - ~"nif_version", - ~"otp_release", - ~"outstanding_system_requests_limit", - ~"port_parallelism", - ~"system_architecture", - ~"system_logger", - ~"system_version", - ~"trace_control_word", - ~"version", - ~"wordsize" - ], + ~"allocated_areas", + % ~"allocator", %% TBD + ~"alloc_util_allocators", + % ~"allocator_sizes", %% TBD + ~"cpu_topology", + ~"logical_processors", + ~"logical_processors_available", + ~"logical_processors_online", + ~"cpu_quota", + ~"update_cpu_info", + ~"fullsweep_after", + ~"garbage_collection", + ~"heap_sizes", + ~"heap_type", + ~"max_heap_size", + ~"message_queue_data", + ~"min_heap_size", + ~"min_bin_vheap_size", + ~"procs", + ~"atom_count", + ~"atom_limit", + ~"ets_count", + ~"ets_limit", + ~"port_count", + ~"port_limit", + ~"process_count", + ~"process_limit", + ~"end_time", + ~"os_monotonic_time_source", + ~"os_system_time_source", + ~"start_time", + ~"time_correction", + ~"time_offset", + ~"time_warp_mode", + ~"tolerant_timeofday", + ~"dirty_cpu_schedulers", + ~"dirty_cpu_schedulers_online", + ~"dirty_io_schedulers", + ~"multi_scheduling", + ~"multi_scheduling_blockers", + ~"normal_multi_scheduling_blockers", + ~"scheduler_bind_type", + ~"scheduler_bindings", + % ~"scheduler_id", %% Intentionally omitted + ~"schedulers", + ~"schedulers_online", + ~"smp_support", + ~"threads", + ~"thread_pool_size", + ~"async_dist", + ~"creation", + ~"delayed_node_table_gc", + ~"dist", + ~"dist_buf_busy_limit", + ~"dist_ctrl", + ~"c_compiler_used", + ~"check_io", + ~"debug_compiled", + ~"driver_version", + ~"dynamic_trace", + ~"dynamic_trace_probes", + ~"emu_flavor", + ~"emu_type", + ~"halt_flush_timeout", + ~"info", + ~"kernel_poll", + ~"loaded", + ~"machine", + ~"modified_timing_level", + ~"nif_version", + ~"otp_release", + ~"outstanding_system_requests_limit", + ~"port_parallelism", + ~"system_architecture", + ~"system_logger", + ~"system_version", + ~"trace_control_word", + ~"version", + ~"wordsize" + ], {ok, {Keys, 0}, State}; open(_FID, [~"system_info", ~"wordsize"], _Mode, State) -> {ok, {[~"internal", ~"external"], 0}, State}; @@ -128,14 +130,15 @@ Data = iolist_to_binary(io_lib:format("~B", [Wordsize])), {ok, {Data, 0}, State}; open(_FID, [~"system_info", KeyB], _Mode, State) -> Key = binary_to_atom(KeyB), - Data = case erlang:system_info(Key) of - Val when is_binary(Val) -> Val; - Val -> - case io_lib:printable_list(Val) of - true -> unicode:characters_to_binary(Val); - false -> iolist_to_binary(io_lib:format("~p", [Val])) - end - end, + Data = + case erlang:system_info(Key) of + Val when is_binary(Val) -> Val; + Val -> + case io_lib:printable_list(Val) of + true -> unicode:characters_to_binary(Val); + false -> iolist_to_binary(io_lib:format("~p", [Val])) + end + end, {ok, {Data, 0}, State}; %% ===== Processes ===== open(_FID, [~"processes"], _Mode, State) -> @@ -143,22 +146,22 @@ Processes = lists:map(fun pid_to_list/1, erlang:processes()), {ok, {Processes, 0}, State}; open(_FID, [~"processes", _PID], _Mode, State) -> Keys = [ - ~"current_function", - ~"initial_call", - ~"status", - ~"message_queue_len", - ~"links", - ~"dictionary", - ~"trap_exit", - ~"error_handler", - ~"priority", - ~"group_leader", - ~"total_heap_size", - ~"heap_size", - ~"stack_size", - ~"reductions", - ~"garbage_collection" - ], + ~"current_function", + ~"initial_call", + ~"status", + ~"message_queue_len", + ~"links", + ~"dictionary", + ~"trap_exit", + ~"error_handler", + ~"priority", + ~"group_leader", + ~"total_heap_size", + ~"heap_size", + ~"stack_size", + ~"reductions", + ~"garbage_collection" + ], {ok, {Keys, 0}, State}; open(_FID, [~"processes", PIDB, KeyB], _Mode, State) -> PIDL = binary_to_list(PIDB), @@ -168,14 +171,17 @@ case erlang:process_info(PID, Key) of {Key, Val} -> Data = iolist_to_binary(io_lib:format("~p", [Val])), {ok, {Data, 0}, State}; - [] -> {ok, {[], 0}, State}; + [] -> + {ok, {[], 0}, State}; undefined -> {error, "No such file", State} end; %% ===== Applications ===== open(_FID, [~"applications"], _Mode, State) -> - AllApps = lists:map(fun({Name, _, _}) when is_atom(Name) -> erlang:atom_to_binary(Name) end, - application:loaded_applications()), + AllApps = lists:map( + fun({Name, _, _}) when is_atom(Name) -> erlang:atom_to_binary(Name) end, + application:loaded_applications() + ), {ok, {AllApps, 0}, State}; open(_FID, [~"applications", Name], _Mode, State) -> Atom = binary_to_existing_atom(Name), @@ -205,23 +211,28 @@ false -> readfile(Data, Path, Offset, Length, State) end. readdir(Data, Path, Offset, Length, State) -> - Encoded = lists:map(fun(Entry) -> - {ok, Stat} = stat_for(Path ++ [Entry]), - e9p_msg:encode_stat(Stat) - end, Data), + Encoded = lists:map( + fun(Entry) -> + {ok, Stat} = stat_for(Path ++ [Entry]), + e9p_msg:encode_stat(Stat) + end, + Data + ), Bin = iolist_to_binary(Encoded), {ok, {Data, chunk(Bin, Offset, Length)}, State}. readfile(Data, _Path, Offset, Length, State) when is_integer(Length) -> {ok, {Data, chunk(Data, Offset, Length)}, State}. -chunk(Data, Offset, Length) - when is_binary(Data), is_integer(Offset), is_integer(Length) -> +chunk(Data, Offset, Length) when + is_binary(Data), is_integer(Offset), is_integer(Length) +-> if Offset =< byte_size(Data) -> Len = min(Length, byte_size(Data) - Offset), binary_part(Data, Offset, Len); - true -> ~"" + true -> + ~"" end. write(_FID, _Path, _Offset, _Data, State) -> @@ -241,111 +252,111 @@ {error, "Not supported", State}. stat_for([]) -> {ok, #{ - qid => e9p:make_qid(directory, 0, 0), - name => ~"/", - mode => 8#555, - length => 0 - }}; + qid => e9p:make_qid(directory, 0, 0), + name => ~"/", + mode => 8#555, + length => 0 + }}; stat_for([~"processes"]) -> {ok, #{ - qid => e9p:make_qid(directory, 0, 1), - name => ~"processes", - mode => 8#555, - length => 0 - }}; + qid => e9p:make_qid(directory, 0, 1), + name => ~"processes", + mode => 8#555, + length => 0 + }}; stat_for([~"processes", PID]) -> Hash = erlang:phash2(PID, 16#FFFF), <> = <<16#1, 0:24, Hash:32>>, {ok, #{ - qid => e9p:make_qid(directory, 0, Value), - name => PID, - mode => 8#555, - length => 0 - }}; + qid => e9p:make_qid(directory, 0, Value), + name => PID, + mode => 8#555, + length => 0 + }}; stat_for([~"processes", PID, Key]) -> Hash = erlang:phash2(PID, 16#FFFF), KeyH = erlang:phash2(Key, 16#FFF), <> = <<16#1, KeyH:24, Hash:32>>, {ok, #{ - qid => e9p:make_qid(regular, 0, Value), - name => Key, - mode => 8#555, - length => 0 - }}; + qid => e9p:make_qid(regular, 0, Value), + name => Key, + mode => 8#555, + length => 0 + }}; stat_for([~"applications"]) -> {ok, #{ - qid => e9p:make_qid(directory, 0, 2), - name => ~"applications", - mode => 8#555, - length => 0 - }}; + qid => e9p:make_qid(directory, 0, 2), + name => ~"applications", + mode => 8#555, + length => 0 + }}; stat_for([~"applications", Name]) -> Atom = binary_to_existing_atom(Name), case application:get_key(Atom, vsn) of - undefined -> {error, "Not exist"}; + undefined -> + {error, "Not exist"}; {ok, _Vsn} -> Hash = erlang:phash2(Name, 16#FFFF), <> = <<16#2, 0:24, Hash:32>>, - {ok, - #{ - qid => e9p:make_qid(directory, 0, Value), - name => Name, - mode => 8#555, - length => 0 - }} + {ok, #{ + qid => e9p:make_qid(directory, 0, Value), + name => Name, + mode => 8#555, + length => 0 + }} end; stat_for([~"applications", Name, Key]) -> Hash = erlang:phash2(Name, 16#FFFF), KeyH = erlang:phash2(Key, 16#FFF), <> = <<16#2, KeyH:24, Hash:32>>, {ok, #{ - qid => e9p:make_qid(regular, 0, Value), - name => Key, - mode => 8#444, - length => 0 - }}; + qid => e9p:make_qid(regular, 0, Value), + name => Key, + mode => 8#444, + length => 0 + }}; stat_for([~"system_info"]) -> {ok, #{ - qid => e9p:make_qid(directory, 0, 3), - name => ~"system_info", - mode => 8#555, - length => 0 - }}; + qid => e9p:make_qid(directory, 0, 3), + name => ~"system_info", + mode => 8#555, + length => 0 + }}; stat_for([~"system_info", ~"wordsize"]) -> Hash = erlang:phash2(~"wordsize", 16#FFFF), <> = <<16#2, 0:24, Hash:32>>, {ok, #{ - qid => e9p:make_qid(directory, 0, Value), - name => ~"wordsize", - mode => 8#555, - length => 0 - }}; + qid => e9p:make_qid(directory, 0, Value), + name => ~"wordsize", + mode => 8#555, + length => 0 + }}; stat_for([~"system_info", ~"wordsize", ~"internal"]) -> Hash = erlang:phash2(~"wordsize", 16#FFFF), <> = <<16#2, 1:24, Hash:32>>, {ok, #{ - qid => e9p:make_qid(regular, 0, Value), - name => ~"internal", - mode => 8#444, - length => 0 - }}; + qid => e9p:make_qid(regular, 0, Value), + name => ~"internal", + mode => 8#444, + length => 0 + }}; stat_for([~"system_info", ~"wordsize", ~"external"]) -> Hash = erlang:phash2(~"wordsize", 16#FFFF), <> = <<16#2, 2:24, Hash:32>>, {ok, #{ - qid => e9p:make_qid(regular, 0, Value), - name => ~"external", - mode => 8#444, - length => 0 - }}; + qid => e9p:make_qid(regular, 0, Value), + name => ~"external", + mode => 8#444, + length => 0 + }}; stat_for([~"system_info", Name]) -> Hash = erlang:phash2(Name, 16#FFFF), <> = <<16#2, 0:24, Hash:32>>, {ok, #{ - qid => e9p:make_qid(regular, 0, Value), - name => Name, - mode => 8#444, - length => 0 - }}; + qid => e9p:make_qid(regular, 0, Value), + name => Name, + mode => 8#444, + length => 0 + }}; stat_for(_Path) -> {error, "Not exist"}. diff --git a/src/e9p_unfs.erl b/src/e9p_unfs.erl --- a/src/e9p_unfs.erl +++ b/src/e9p_unfs.erl @@ -13,8 +13,19 @@ -include_lib("kernel/include/logger.hrl"). -include_lib("kernel/include/file.hrl"). --export([init/1, root/3, walk/4, stat/3, open/4, read/5, clunk/2, create/6, - write/5, remove/3, wstat/4]). +-export([ + init/1, + root/3, + walk/4, + stat/3, + open/4, + read/5, + clunk/2, + create/6, + write/5, + remove/3, + wstat/4 +]). % Create QID and Stat data for given path. qid(Root, Path) -> @@ -24,42 +35,45 @@ {ok, #file_info{type = Type, inode = Inode} = FI} -> QID = e9p:make_qid(Type, 0, Inode), Stat = file_info_to_stat(Path, QID, FI), {ok, QID, Stat}; - {error, _} = Error -> Error + {error, _} = Error -> + Error end. file_info_to_stat( - Path, - QID, - #file_info{ - size = Len, - atime = Atime, - mtime = Mtime, - mode = Mode - }) -> - Name = if - Path == [] -> ~"/"; - true -> lists:last(Path) - end, + Path, + QID, + #file_info{ + size = Len, + atime = Atime, + mtime = Mtime, + mode = Mode + } +) -> + Name = + if + Path == [] -> ~"/"; + true -> lists:last(Path) + end, #{ - qid => QID, - mode => Mode, - atime => Atime, - mtime => Mtime, - length => Len, - name => Name - }. + qid => QID, + mode => Mode, + atime => Atime, + mtime => Mtime, + length => Len, + name => Name + }. stat_to_file_info(Stat) -> #{ - mode := Mode, - atime := Atime, - mtime := Mtime - } = Stat, + mode := Mode, + atime := Atime, + mtime := Mtime + } = Stat, #file_info{ - mode = Mode, - atime = Atime, - mtime = Mtime - }. + mode = Mode, + atime = Atime, + mtime = Mtime + }. %% ====== Filesystem handlers ====== @@ -105,30 +119,32 @@ FullPath = filename:join([Root] ++ Path), case file:write_file_info(FullPath, FileInfo, [{time, posix}, raw]) of ok -> {ok, State}; - {error, Reason} -> - {error, io_lib:format("Couldn't write file stat: ~p", [Reason]), - State} + {error, Reason} -> {error, io_lib:format("Couldn't write file stat: ~p", [Reason]), State} end. -doc false. open({QID, []}, Path, Mode, #{root := Root} = State) -> FullPath = filename:join([Root] ++ Path), - QS = case e9p:is_type(QID, directory) of - true -> - % Currently `file` module do not expose raw mode for listing - % file directory, so we need to call private `prim_file` module - % to access such functionality. Otherwise we can encounter - % deadlock. - % - % See: https://github.com/erlang/otp/issues/10593 - {ok, List} = prim_file:list_dir(FullPath), - {dir, List}; - false -> - {Trunc, Opts} = translate_mode(Mode), - {ok, FD} = file:open(FullPath, [raw, binary | Opts]), - if Trunc -> file:truncate(FD); true -> ok end, - {regular, FD} - end, + QS = + case e9p:is_type(QID, directory) of + true -> + % Currently `file` module do not expose raw mode for listing + % file directory, so we need to call private `prim_file` module + % to access such functionality. Otherwise we can encounter + % deadlock. + % + % See: https://github.com/erlang/otp/issues/10593 + {ok, List} = prim_file:list_dir(FullPath), + {dir, List}; + false -> + {Trunc, Opts} = translate_mode(Mode), + {ok, FD} = file:open(FullPath, [raw, binary | Opts]), + if + Trunc -> file:truncate(FD); + true -> ok + end, + {regular, FD} + end, {ok, {QS, 0}, State}. -doc false. @@ -146,28 +162,33 @@ -doc false. remove({QID, _} = FID, Path, #{root := Root} = State0) -> FullPath = filename:join([Root] ++ Path), {ok, State} = clunk(FID, State0), - case case e9p:is_type(QID, directory) of - % Currently `file` module do not expose raw mode for listing - % file directory, so we need to call private `prim_file` module - % to access such functionality. Otherwise we can encounter - % deadlock. - % - % See: https://github.com/erlang/otp/issues/10593 - true -> prim_file:del_dir(FullPath); - false -> file:delete(FullPath, [raw]) - end of + case + case e9p:is_type(QID, directory) of + % Currently `file` module do not expose raw mode for listing + % file directory, so we need to call private `prim_file` module + % to access such functionality. Otherwise we can encounter + % deadlock. + % + % See: https://github.com/erlang/otp/issues/10593 + true -> prim_file:del_dir(FullPath); + false -> file:delete(FullPath, [raw]) + end + of ok -> {ok, State}; - {error, Reason} -> - {error, io_lib:format("Failed to remove path: ~p", [Reason]), State} + {error, Reason} -> {error, io_lib:format("Failed to remove path: ~p", [Reason]), State} end. translate_mode([trunc | Rest]) -> {_, Mode} = translate_mode(Rest), {true, Mode}; -translate_mode([read]) -> {false, [read]}; -translate_mode([write]) -> {false, [read, write]}; -translate_mode([append]) -> {false, [read, write]}; -translate_mode([exec]) -> {false, [read]}. +translate_mode([read]) -> + {false, [read]}; +translate_mode([write]) -> + {false, [read, write]}; +translate_mode([append]) -> + {false, [read, write]}; +translate_mode([exec]) -> + {false, [read]}. -doc false. read({_QID, {regular, FD}}, _Path, Offset, Len, State) -> @@ -180,8 +201,10 @@ read({_QID, {dir, List}}, Path, _Offset, Len, #{root := Root} = State) -> {Remaining, Data} = readdir(Root, Path, List, Len, []), {ok, {{dir, Remaining}, Data}, State}. -readdir(_Root, _Path, List, 0, Acc) -> {List, Acc}; -readdir(_Root, _Path, [], _Len, Acc) -> {[], Acc}; +readdir(_Root, _Path, List, 0, Acc) -> + {List, Acc}; +readdir(_Root, _Path, [], _Len, Acc) -> + {[], Acc}; readdir(Root, Path, [Next | Rest], Len, Acc) -> {ok, _QID, Stat} = qid(Root, Path ++ [Next]), Encoded = e9p_msg:encode_stat(Stat), diff --git a/test/e9p_msg_SUITE.erl b/test/e9p_msg_SUITE.erl --- a/test/e9p_msg_SUITE.erl +++ b/test/e9p_msg_SUITE.erl @@ -11,47 +11,48 @@ -include_lib("stdlib/include/assert.hrl"). -include_lib("common_test/include/ct.hrl"). -all() -> [ - stat_encode_decode, - rstat_encode_decode, - rclunk_encode_decode, - rflush_encode_decode, - rwstat_encode_decode, - rremove_encode_decode - ]. +all() -> + [ + stat_encode_decode, + rstat_encode_decode, + rclunk_encode_decode, + rflush_encode_decode, + rwstat_encode_decode, + rremove_encode_decode + ]. stat_encode_decode(_Conf) -> Stat = #{ - type => 0, - dev => 0, - qid => e9p:make_qid(regular, 0, 0), - mode => 0, - atime => 0, - mtime => 0, - length => 0, - name => <<>>, - uid => <<>>, - gid => <<>>, - muid => <<>> - }, + type => 0, + dev => 0, + qid => e9p:make_qid(regular, 0, 0), + mode => 0, + atime => 0, + mtime => 0, + length => 0, + name => <<>>, + uid => <<>>, + gid => <<>>, + muid => <<>> + }, Out = iolist_to_binary(e9p_msg:encode_stat(Stat)), Decoded = e9p_msg:parse_stat(Out), ?assertEqual({ok, Stat}, Decoded). rstat_encode_decode(_Conf) -> Stat = #{ - type => 0, - dev => 0, - qid => e9p:make_qid(regular, 0, 0), - mode => 0, - atime => 0, - mtime => 0, - length => 0, - name => <<>>, - uid => <<>>, - gid => <<>>, - muid => <<>> - }, + type => 0, + dev => 0, + qid => e9p:make_qid(regular, 0, 0), + mode => 0, + atime => 0, + mtime => 0, + length => 0, + name => <<>>, + uid => <<>>, + gid => <<>>, + muid => <<>> + }, Msg = #rstat{stat = Stat}, Tag = 1, Out = e9p_msg:encode(Tag, Msg), diff --git a/test/e9p_sysfs_SUITE.erl b/test/e9p_sysfs_SUITE.erl --- a/test/e9p_sysfs_SUITE.erl +++ b/test/e9p_sysfs_SUITE.erl @@ -9,15 +9,16 @@ -include_lib("stdlib/include/assert.hrl"). -include_lib("common_test/include/ct.hrl"). -all() -> [ - can_list_mount_content, - current_process_is_listed, - current_process_current_function, - system_info_atom_count, - applications_list, - application_info, - application_env - ]. +all() -> + [ + can_list_mount_content, + current_process_is_listed, + current_process_current_function, + system_info_atom_count, + applications_list, + application_info, + application_env + ]. init_per_suite(Config) -> PrivDir = ?config(priv_dir, Config), @@ -26,10 +27,12 @@ Port = 19999, ok = file:make_dir(Path), {ok, PID} = e9p_server:start(Port, {e9p_sysfs, []}), ct:pal(Path), - Cmd = io_lib:format("9pfs -p ~B localhost ~s", - [Port, Path]), + Cmd = io_lib:format( + "9pfs -p ~B localhost ~s", + [Port, Path] + ), ct:pal(Cmd), - _Out = os:cmd(Cmd, #{ exception_on_failure => true }), + _Out = os:cmd(Cmd, #{exception_on_failure => true}), [{sysfs, PID}, {mount, Path} | Config]. end_per_suite(Config) -> @@ -42,97 +45,110 @@ can_list_mount_content(Config) -> Mount = ?config(mount, Config), ct:pal(Mount), - ?assertEqual([ - "applications", - "processes", - "system_info" - ], ls(Mount)). + ?assertEqual( + [ + "applications", + "processes", + "system_info" + ], + ls(Mount) + ). current_process_is_listed(Config) -> Mount = ?config(mount, Config), Path = filename:join([Mount, "processes", pid_to_list(self())]), - ?assertEqual([ - "current_function", - "dictionary", - "error_handler", - "garbage_collection", - "group_leader", - "heap_size", - "initial_call", - "links", - "message_queue_len", - "priority", - "reductions", - "stack_size", - "status", - "total_heap_size", - "trap_exit" - ], ls(Path)). + ?assertEqual( + [ + "current_function", + "dictionary", + "error_handler", + "garbage_collection", + "group_leader", + "heap_size", + "initial_call", + "links", + "message_queue_len", + "priority", + "reductions", + "stack_size", + "status", + "total_heap_size", + "trap_exit" + ], + ls(Path) + ). current_process_current_function(Config) -> Mount = ?config(mount, Config), Path = filename:join([ - Mount, - "processes", - pid_to_list(self()), - "current_function" - ]), + Mount, + "processes", + pid_to_list(self()), + "current_function" + ]), {ok, Bin} = file:read_file(Path), ?assertEqual(~"{gen,do_call,4}", Bin). system_info_atom_count(Config) -> Mount = ?config(mount, Config), Path = filename:join([ - Mount, - "system_info", - "atom_count" - ]), + Mount, + "system_info", + "atom_count" + ]), {ok, Bin} = file:read_file(Path), ?assertEqual(erlang:system_info(atom_count), binary_to_integer(Bin)). applications_list(Config) -> Mount = ?config(mount, Config), Path = filename:join([ - Mount, - "applications" - ]), + Mount, + "applications" + ]), List = ls(Path), - Apps = lists:sort(lists:map(fun({AppName, _, _}) -> atom_to_list(AppName) end, - application:loaded_applications())), + Apps = lists:sort( + lists:map( + fun({AppName, _, _}) -> atom_to_list(AppName) end, + application:loaded_applications() + ) + ), ?assertEqual(Apps, List). application_info(Config) -> Mount = ?config(mount, Config), Path = filename:join([ - Mount, - "applications", - "kernel" - ]), + Mount, + "applications", + "kernel" + ]), List = ls(Path), - ?assertEqual([ - "applications", - "description", - "env", - "id", - "included_applications", - "maxP", - "maxT", - "mod", - "modules", - "optional_applications", - "registered", - "start_phases", - "vsn" - ], List). + ?assertEqual( + [ + "applications", + "description", + "env", + "id", + "included_applications", + "maxP", + "maxT", + "mod", + "modules", + "optional_applications", + "registered", + "start_phases", + "vsn" + ], + List + ). application_env(Config) -> Mount = ?config(mount, Config), Path = filename:join([ - Mount, - "applications", - "kernel", - "env" - ]), + Mount, + "applications", + "kernel", + "env" + ]), {ok, [Read]} = file:consult(Path), Env = application:get_all_env(kernel), ?assertEqual(Read, Env). diff --git a/test/e9p_unfs_SUITE.erl b/test/e9p_unfs_SUITE.erl --- a/test/e9p_unfs_SUITE.erl +++ b/test/e9p_unfs_SUITE.erl @@ -5,13 +5,14 @@ -include_lib("stdlib/include/assert.hrl"). -include_lib("common_test/include/ct.hrl"). -all() -> [ - list_mount, - read_file - %% Ignore this test for now, as UID/GID translation is not possible - %% right now until we implement 9p2000.u extension - % write_to_existing_file - ]. +all() -> + [ + list_mount, + read_file + %% Ignore this test for now, as UID/GID translation is not possible + %% right now until we implement 9p2000.u extension + % write_to_existing_file + ]. init_per_suite(Config) -> PrivDir = ?config(priv_dir, Config), @@ -23,10 +24,12 @@ ok = file:make_dir(Mount), {ok, PID} = e9p_server:start(Port, {e9p_unfs, #{path => Source}}), ct:pal("source: ~s", [Source]), ct:pal("mount: ~s", [Mount]), - Cmd = io_lib:format("9pfs -p ~B localhost ~p", - [Port, Mount]), + Cmd = io_lib:format( + "9pfs -p ~B localhost ~p", + [Port, Mount] + ), ct:pal(Cmd), - _Out = os:cmd(Cmd, #{ exception_on_failure => true }), + _Out = os:cmd(Cmd, #{exception_on_failure => true}), [{fs, PID}, {mount, Mount}, {source, Source} | Config]. end_per_suite(Config) -> diff --git a/test/prop_e9p_msg.erl b/test/prop_e9p_msg.erl --- a/test/prop_e9p_msg.erl +++ b/test/prop_e9p_msg.erl @@ -15,126 +15,213 @@ integer(Min, Max). fid() -> int(2). -bin_str() -> ?LET({Charlist}, {string()}, - unicode:characters_to_binary(Charlist)). +bin_str() -> + ?LET( + {Charlist}, + {string()}, + unicode:characters_to_binary(Charlist) + ). -qid_type() -> union([directory, - append, - excl, - device, - auth, - tmp, - symlink, - regular - ]). +qid_type() -> + union([ + directory, + append, + excl, + device, + auth, + tmp, + symlink, + regular + ]). qid() -> - ?LET({Type, Version, Path}, {qid_type(), int(4), int(8)}, - e9p:make_qid(Type, Version, Path)). + ?LET( + {Type, Version, Path}, + {qid_type(), int(4), int(8)}, + e9p:make_qid(Type, Version, Path) + ). prop_tversion() -> - ?FORALL({Version, MPS}, {bin_str(), int(4)}, - enc_dec(#tversion{version = Version, max_packet_size = MPS})). + ?FORALL( + {Version, MPS}, + {bin_str(), int(4)}, + enc_dec(#tversion{version = Version, max_packet_size = MPS}) + ). prop_rversion() -> - ?FORALL({Version, MPS}, {bin_str(), fid()}, - enc_dec(#rversion{version = Version, max_packet_size = MPS})). + ?FORALL( + {Version, MPS}, + {bin_str(), fid()}, + enc_dec(#rversion{version = Version, max_packet_size = MPS}) + ). prop_tauth() -> - ?FORALL({Afid, Uname, Aname}, {fid(), bin_str(), bin_str()}, - enc_dec(#tauth{afid = Afid, - uname = Uname, - aname = Aname})). + ?FORALL( + {Afid, Uname, Aname}, + {fid(), bin_str(), bin_str()}, + enc_dec(#tauth{ + afid = Afid, + uname = Uname, + aname = Aname + }) + ). prop_rauth() -> - ?FORALL({AQID}, {qid()}, - enc_dec(#rauth{aqid = AQID})). + ?FORALL( + {AQID}, + {qid()}, + enc_dec(#rauth{aqid = AQID}) + ). prop_rerror() -> - ?FORALL({Msg}, {bin_str()}, - begin - enc_dec(#rerror{msg = Msg}) - end). + ?FORALL( + {Msg}, + {bin_str()}, + begin + enc_dec(#rerror{msg = Msg}) + end + ). prop_tattach() -> - ?FORALL({FID, AFID, Uname, Aname}, {fid(), fid(), bin_str(), bin_str()}, - enc_dec(#tattach{ - fid = FID, - afid = AFID, - uname = Uname, - aname = Aname - })). + ?FORALL( + {FID, AFID, Uname, Aname}, + {fid(), fid(), bin_str(), bin_str()}, + enc_dec(#tattach{ + fid = FID, + afid = AFID, + uname = Uname, + aname = Aname + }) + ). prop_rattach() -> - ?FORALL({QID}, {qid()}, - enc_dec(#rattach{qid = QID})). + ?FORALL( + {QID}, + {qid()}, + enc_dec(#rattach{qid = QID}) + ). prop_twalk() -> - ?FORALL({FID, NewFID, Names}, {fid(), fid(), list(bin_str())}, - enc_dec(#twalk{fid = FID, new_fid = NewFID, names = Names})). + ?FORALL( + {FID, NewFID, Names}, + {fid(), fid(), list(bin_str())}, + enc_dec(#twalk{fid = FID, new_fid = NewFID, names = Names}) + ). prop_rwalk() -> - ?FORALL({QIDs}, {list(qid())}, - enc_dec(#rwalk{qids = QIDs})). + ?FORALL( + {QIDs}, + {list(qid())}, + enc_dec(#rwalk{qids = QIDs}) + ). prop_topen() -> - ?FORALL({FID, Mode}, {fid(), int(1)}, - enc_dec(#topen{fid = FID, mode = Mode})). + ?FORALL( + {FID, Mode}, + {fid(), int(1)}, + enc_dec(#topen{fid = FID, mode = Mode}) + ). prop_ropen() -> - ?FORALL({QID, IOUnit}, {qid(), int(4)}, - enc_dec(#ropen{qid = QID, io_unit = IOUnit})). + ?FORALL( + {QID, IOUnit}, + {qid(), int(4)}, + enc_dec(#ropen{qid = QID, io_unit = IOUnit}) + ). prop_tcreate() -> - ?FORALL({FID, Name, Perm, Mode}, {fid(), bin_str(), int(4), int(1)}, - enc_dec(#tcreate{fid = FID, - name = Name, - perm = Perm, - mode = Mode})). + ?FORALL( + {FID, Name, Perm, Mode}, + {fid(), bin_str(), int(4), int(1)}, + enc_dec(#tcreate{ + fid = FID, + name = Name, + perm = Perm, + mode = Mode + }) + ). prop_rcreate() -> - ?FORALL({QID, IOUnit}, {qid(), int(4)}, - enc_dec(#rcreate{qid = QID, io_unit = IOUnit})). + ?FORALL( + {QID, IOUnit}, + {qid(), int(4)}, + enc_dec(#rcreate{qid = QID, io_unit = IOUnit}) + ). prop_tremove() -> - ?FORALL({FID}, {fid()}, - enc_dec(#tremove{fid = FID})). + ?FORALL( + {FID}, + {fid()}, + enc_dec(#tremove{fid = FID}) + ). prop_tclunk() -> - ?FORALL({FID}, {fid()}, - enc_dec(#tclunk{fid = FID})). + ?FORALL( + {FID}, + {fid()}, + enc_dec(#tclunk{fid = FID}) + ). prop_tread() -> - ?FORALL({FID, Offset, Len}, {fid(), int(8), int(4)}, - enc_dec(#tread{fid = FID, offset = Offset, len = Len})). + ?FORALL( + {FID, Offset, Len}, + {fid(), int(8), int(4)}, + enc_dec(#tread{fid = FID, offset = Offset, len = Len}) + ). prop_rread() -> - ?FORALL({Data}, {binary()}, - enc_dec(#rread{data = Data})). + ?FORALL( + {Data}, + {binary()}, + enc_dec(#rread{data = Data}) + ). prop_twrite() -> - ?FORALL({FID, Offset, Data}, {fid(), int(8), binary()}, - enc_dec(#twrite{fid = FID, offset = Offset, data = Data})). + ?FORALL( + {FID, Offset, Data}, + {fid(), int(8), binary()}, + enc_dec(#twrite{fid = FID, offset = Offset, data = Data}) + ). prop_rwrite() -> - ?FORALL({Len}, {int(4)}, - enc_dec(#rwrite{len = Len})). + ?FORALL( + {Len}, + {int(4)}, + enc_dec(#rwrite{len = Len}) + ). prop_tstat() -> - ?FORALL({FID}, {fid()}, - enc_dec(#tstat{fid = FID})). + ?FORALL( + {FID}, + {fid()}, + enc_dec(#tstat{fid = FID}) + ). prop_rstat() -> - ?FORALL({QID, Type, Dev, Mode, Atime, Mtime, Len, Name, Uid, Gid, Muid}, - {qid(), int(2), int(2), integer(0, 8#777), int(4), int(4), int(8), bin_str(), bin_str(), - bin_str(), bin_str()}, - begin - enc_dec(#rstat{ - stat = #{ - type => Type, - dev => Dev, - qid => QID, - mode => Mode, - atime => Atime, - mtime => Mtime, - length => Len, - name => Name, - uid => Uid, - gid => Gid, - muid => Muid - }}) - end). + ?FORALL( + {QID, Type, Dev, Mode, Atime, Mtime, Len, Name, Uid, Gid, Muid}, + { + qid(), + int(2), + int(2), + integer(0, 8#777), + int(4), + int(4), + int(8), + bin_str(), + bin_str(), + bin_str(), + bin_str() + }, + begin + enc_dec(#rstat{ + stat = #{ + type => Type, + dev => Dev, + qid => QID, + mode => Mode, + atime => Atime, + mtime => Mtime, + length => Len, + name => Name, + uid => Uid, + gid => Gid, + muid => Muid + } + }) + end + ). prop_tflush() -> ?FORALL({Tag}, {int(2)}, enc_dec(#tflush{tag = Tag})).