diff --git a/apps/emqx_conf/src/emqx_conf_schema.erl b/apps/emqx_conf/src/emqx_conf_schema.erl index 76213c690..b37c3f71e 100644 --- a/apps/emqx_conf/src/emqx_conf_schema.erl +++ b/apps/emqx_conf/src/emqx_conf_schema.erl @@ -508,6 +508,7 @@ fields("node") -> desc => ?DESC(node_crash_dump_file), default => crash_dump_file_default(), importance => ?IMPORTANCE_HIDDEN, + converter => fun ensure_unicode_path/2, 'readOnly' => true } )}, @@ -754,6 +755,7 @@ fields("rpc") -> file(), #{ mapping => "gen_rpc.certfile", + converter => fun ensure_unicode_path/2, desc => ?DESC(rpc_certfile) } )}, @@ -762,6 +764,7 @@ fields("rpc") -> file(), #{ mapping => "gen_rpc.keyfile", + converter => fun ensure_unicode_path/2, desc => ?DESC(rpc_keyfile) } )}, @@ -770,6 +773,7 @@ fields("rpc") -> file(), #{ mapping => "gen_rpc.cacertfile", + converter => fun ensure_unicode_path/2, desc => ?DESC(rpc_cacertfile) } )}, @@ -892,8 +896,10 @@ fields("log_file_handler") -> file(), #{ desc => ?DESC("log_file_handler_file"), - default => <<"${EMQX_LOG_DIR}/emqx.log">>, - converter => fun emqx_schema:naive_env_interpolation/1 + converter => fun(Path, Opts) -> + emqx_schema:naive_env_interpolation(ensure_unicode_path(Path, Opts)) + end, + default => <<"${EMQX_LOG_DIR}/emqx.log">> } )}, {"rotation", @@ -1349,3 +1355,20 @@ validator_string_re(Val, RE, Error) -> node_array() -> hoconsc:union([emqx_schema:comma_separated_atoms(), hoconsc:array(atom())]). + +ensure_unicode_path(undefined, _) -> + undefined; +ensure_unicode_path(Path, #{make_serializable := true}) -> + %% format back to serializable string + unicode:characters_to_binary(Path, utf8); +ensure_unicode_path(Path, Opts) when is_binary(Path) -> + case unicode:characters_to_list(Path, utf8) of + {R, _, _} when R =:= error orelse R =:= incomplete -> + throw({"bad_file_path_string", Path}); + PathStr -> + ensure_unicode_path(PathStr, Opts) + end; +ensure_unicode_path(Path, _) when is_list(Path) -> + Path; +ensure_unicode_path(Path, _) -> + throw({"not_string", Path}). diff --git a/apps/emqx_conf/test/emqx_conf_schema_tests.erl b/apps/emqx_conf/test/emqx_conf_schema_tests.erl index 06784e32d..4eaf3db6b 100644 --- a/apps/emqx_conf/test/emqx_conf_schema_tests.erl +++ b/apps/emqx_conf/test/emqx_conf_schema_tests.erl @@ -232,3 +232,47 @@ ensure_acl_conf() -> true -> ok; false -> file:write_file(File, <<"">>) end. + +log_path_test_() -> + Fh = fun(Path) -> + #{<<"log">> => #{<<"file_handlers">> => #{<<"name1">> => #{<<"file">> => Path}}}} + end, + Assert = fun(Name, Path, Conf) -> + ?assertMatch(#{log := #{file_handlers := #{Name := #{file := Path}}}}, Conf) + end, + + [ + {"default-values", fun() -> Assert(default, "log/emqx.log", check(#{})) end}, + {"file path with space", fun() -> Assert(name1, "a /b", check(Fh(<<"a /b">>))) end}, + {"windows", fun() -> Assert(name1, "c:\\a\\ b\\", check(Fh(<<"c:\\a\\ b\\">>))) end}, + {"unicoded", fun() -> Assert(name1, "路 径", check(Fh(<<"路 径"/utf8>>))) end}, + {"bad utf8", fun() -> + ?assertThrow( + {emqx_conf_schema, [ + #{ + kind := validation_error, + reason := {"bad_file_path_string", _} + } + ]}, + check(Fh(<<239, 32, 132, 47, 117, 116, 102, 56>>)) + ) + end}, + {"not string", fun() -> + ?assertThrow( + {emqx_conf_schema, [ + #{ + kind := validation_error, + reason := {"not_string", _} + } + ]}, + check(Fh(#{<<"foo">> => <<"bar">>})) + ) + end} + ]. + +check(Config) -> + Schema = emqx_conf_schema, + {_, Conf} = hocon_tconf:map(Schema, Config, [log], #{ + atom_key => false, required => false, format => map + }), + emqx_utils_maps:unsafe_atom_key_map(Conf).