diff options
Diffstat (limited to 'lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_bup.erl')
-rw-r--r-- | lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_bup.erl | 1169 |
1 files changed, 0 insertions, 1169 deletions
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_bup.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_bup.erl deleted file mode 100644 index f03dc029cc..0000000000 --- a/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_bup.erl +++ /dev/null @@ -1,1169 +0,0 @@ -%% ``The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved via the world wide web at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. -%% -%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. -%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings -%% AB. All Rights Reserved.'' -%% -%% $Id: mnesia_bup.erl,v 1.1 2008/12/17 09:53:37 mikpe Exp $ -%% --module(mnesia_bup). --export([ - %% Public interface - iterate/4, - read_schema/2, - fallback_bup/0, - fallback_exists/0, - tm_fallback_start/1, - create_schema/1, - install_fallback/1, - install_fallback/2, - uninstall_fallback/0, - uninstall_fallback/1, - traverse_backup/4, - traverse_backup/6, - make_initial_backup/3, - fallback_to_schema/0, - lookup_schema/2, - schema2bup/1, - refresh_cookie/2, - - %% Internal - fallback_receiver/2, - install_fallback_master/2, - uninstall_fallback_master/2, - local_uninstall_fallback/2, - do_traverse_backup/7, - trav_apply/4 - ]). - --include("mnesia.hrl"). --import(mnesia_lib, [verbose/2, dbg_out/2]). - --record(restore, {mode, bup_module, bup_data}). - --record(fallback_args, {opaque, - scope = global, - module = mnesia_monitor:get_env(backup_module), - use_default_dir = true, - mnesia_dir, - fallback_bup, - fallback_tmp, - skip_tables = [], - keep_tables = [], - default_op = keep_tables - }). - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% Backup iterator - -%% Reads schema section and iterates over all records in a backup. -%% -%% Fun(BunchOfRecords, Header, Schema, Acc) is applied when a suitable amount -%% of records has been collected. -%% -%% BunchOfRecords will be [] when the iteration is done. -iterate(Mod, Fun, Opaque, Acc) -> - R = #restore{bup_module = Mod, bup_data = Opaque}, - case catch read_schema_section(R) of - {error, Reason} -> - {error, Reason}; - {R2, {Header, Schema, Rest}} -> - case catch iter(R2, Header, Schema, Fun, Acc, Rest) of - {ok, R3, Res} -> - catch safe_apply(R3, close_read, [R3#restore.bup_data]), - {ok, Res}; - {error, Reason} -> - catch safe_apply(R2, close_read, [R2#restore.bup_data]), - {error, Reason}; - {'EXIT', Pid, Reason} -> - catch safe_apply(R2, close_read, [R2#restore.bup_data]), - {error, {'EXIT', Pid, Reason}}; - {'EXIT', Reason} -> - catch safe_apply(R2, close_read, [R2#restore.bup_data]), - {error, {'EXIT', Reason}} - end - end. - -iter(R, Header, Schema, Fun, Acc, []) -> - case safe_apply(R, read, [R#restore.bup_data]) of - {R2, []} -> - Res = Fun([], Header, Schema, Acc), - {ok, R2, Res}; - {R2, BupItems} -> - iter(R2, Header, Schema, Fun, Acc, BupItems) - end; -iter(R, Header, Schema, Fun, Acc, BupItems) -> - Acc2 = Fun(BupItems, Header, Schema, Acc), - iter(R, Header, Schema, Fun, Acc2, []). - -safe_apply(R, write, [_, Items]) when Items == [] -> - R; -safe_apply(R, What, Args) -> - Abort = fun(Re) -> abort_restore(R, What, Args, Re) end, - receive - {'EXIT', Pid, Re} -> Abort({'EXIT', Pid, Re}) - after 0 -> - Mod = R#restore.bup_module, - case catch apply(Mod, What, Args) of - {ok, Opaque, Items} when What == read -> - {R#restore{bup_data = Opaque}, Items}; - {ok, Opaque} when What /= read-> - R#restore{bup_data = Opaque}; - {error, Re} -> - Abort(Re); - Re -> - Abort(Re) - end - end. - -abort_restore(R, What, Args, Reason) -> - Mod = R#restore.bup_module, - Opaque = R#restore.bup_data, - dbg_out("Restore aborted. ~p:~p~p -> ~p~n", - [Mod, What, Args, Reason]), - catch apply(Mod, close_read, [Opaque]), - throw({error, Reason}). - -fallback_to_schema() -> - Fname = fallback_bup(), - fallback_to_schema(Fname). - -fallback_to_schema(Fname) -> - Mod = mnesia_backup, - case read_schema(Mod, Fname) of - {error, Reason} -> - {error, Reason}; - Schema -> - case catch lookup_schema(schema, Schema) of - {error, _} -> - {error, "No schema in fallback"}; - List -> - {ok, fallback, List} - end - end. - -%% Opens Opaque reads schema and then close -read_schema(Mod, Opaque) -> - R = #restore{bup_module = Mod, bup_data = Opaque}, - case catch read_schema_section(R) of - {error, Reason} -> - {error, Reason}; - {R2, {_Header, Schema, _}} -> - catch safe_apply(R2, close_read, [R2#restore.bup_data]), - Schema - end. - -%% Open backup media and extract schema -%% rewind backup media and leave it open -%% Returns {R, {Header, Schema}} -read_schema_section(R) -> - case catch do_read_schema_section(R) of - {'EXIT', Reason} -> - catch safe_apply(R, close_read, [R#restore.bup_data]), - {error, {'EXIT', Reason}}; - {error, Reason} -> - catch safe_apply(R, close_read, [R#restore.bup_data]), - {error, Reason}; - {R2, {H, Schema, Rest}} -> - Schema2 = convert_schema(H#log_header.log_version, Schema), - {R2, {H, Schema2, Rest}} - end. - -do_read_schema_section(R) -> - R2 = safe_apply(R, open_read, [R#restore.bup_data]), - {R3, RawSchema} = safe_apply(R2, read, [R2#restore.bup_data]), - do_read_schema_section(R3, verify_header(RawSchema), []). - -do_read_schema_section(R, {ok, B, C, []}, Acc) -> - case safe_apply(R, read, [R#restore.bup_data]) of - {R2, []} -> - {R2, {B, Acc, []}}; - {R2, RawSchema} -> - do_read_schema_section(R2, {ok, B, C, RawSchema}, Acc) - end; - -do_read_schema_section(R, {ok, B, C, [Head | Tail]}, Acc) - when element(1, Head) == schema -> - do_read_schema_section(R, {ok, B, C, Tail}, Acc ++ [Head]); - -do_read_schema_section(R, {ok, B, _C, Rest}, Acc) -> - {R, {B, Acc, Rest}}; - -do_read_schema_section(_R, {error, Reason}, _Acc) -> - {error, Reason}. - -verify_header([H | RawSchema]) when record(H, log_header) -> - Current = mnesia_log:backup_log_header(), - if - H#log_header.log_kind == Current#log_header.log_kind -> - Versions = ["0.1", "1.1", Current#log_header.log_version], - case lists:member(H#log_header.log_version, Versions) of - true -> - {ok, H, Current, RawSchema}; - false -> - {error, {"Bad header version. Cannot be used as backup.", H}} - end; - true -> - {error, {"Bad kind of header. Cannot be used as backup.", H}} - end; -verify_header(RawSchema) -> - {error, {"Missing header. Cannot be used as backup.", catch hd(RawSchema)}}. - -refresh_cookie(Schema, NewCookie) -> - case lists:keysearch(schema, 2, Schema) of - {value, {schema, schema, List}} -> - Cs = mnesia_schema:list2cs(List), - Cs2 = Cs#cstruct{cookie = NewCookie}, - Item = {schema, schema, mnesia_schema:cs2list(Cs2)}, - lists:keyreplace(schema, 2, Schema, Item); - - false -> - Reason = "No schema found. Cannot be used as backup.", - throw({error, {Reason, Schema}}) - end. - -%% Convert schema items from an external backup -%% If backup format is the latest, no conversion is needed -%% All supported backup formats should have their converters -%% here as separate function clauses. -convert_schema("0.1", Schema) -> - convert_0_1(Schema); -convert_schema("1.1", Schema) -> - %% The new backup format is a pure extension of the old one - Current = mnesia_log:backup_log_header(), - convert_schema(Current#log_header.log_version, Schema); -convert_schema(Latest, Schema) -> - H = mnesia_log:backup_log_header(), - if - H#log_header.log_version == Latest -> - Schema; - true -> - Reason = "Bad backup header version. Cannot convert schema.", - throw({error, {Reason, H}}) - end. - -%% Backward compatibility for 0.1 -convert_0_1(Schema) -> - case lists:keysearch(schema, 2, Schema) of - {value, {schema, schema, List}} -> - Schema2 = lists:keydelete(schema, 2, Schema), - Cs = mnesia_schema:list2cs(List), - convert_0_1(Schema2, [], Cs); - false -> - List = mnesia_schema:get_initial_schema(disc_copies, [node()]), - Cs = mnesia_schema:list2cs(List), - convert_0_1(Schema, [], Cs) - end. - -convert_0_1([{schema, cookie, Cookie} | Schema], Acc, Cs) -> - convert_0_1(Schema, Acc, Cs#cstruct{cookie = Cookie}); -convert_0_1([{schema, db_nodes, DbNodes} | Schema], Acc, Cs) -> - convert_0_1(Schema, Acc, Cs#cstruct{disc_copies = DbNodes}); -convert_0_1([{schema, version, Version} | Schema], Acc, Cs) -> - convert_0_1(Schema, Acc, Cs#cstruct{version = Version}); -convert_0_1([{schema, Tab, Def} | Schema], Acc, Cs) -> - Head = - case lists:keysearch(index, 1, Def) of - {value, {index, PosList}} -> - %% Remove the snmp "index" - P = PosList -- [snmp], - Def2 = lists:keyreplace(index, 1, Def, {index, P}), - {schema, Tab, Def2}; - false -> - {schema, Tab, Def} - end, - convert_0_1(Schema, [Head | Acc], Cs); -convert_0_1([Head | Schema], Acc, Cs) -> - convert_0_1(Schema, [Head | Acc], Cs); -convert_0_1([], Acc, Cs) -> - [schema2bup({schema, schema, Cs}) | Acc]. - -%% Returns Val or throw error -lookup_schema(Key, Schema) -> - case lists:keysearch(Key, 2, Schema) of - {value, {schema, Key, Val}} -> Val; - false -> throw({error, {"Cannot lookup", Key}}) - end. - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% Backup compatibility - -%% Convert internal schema items to backup dito -schema2bup({schema, Tab}) -> - {schema, Tab}; -schema2bup({schema, Tab, TableDef}) -> - {schema, Tab, mnesia_schema:cs2list(TableDef)}. - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% Create schema on the given nodes -%% Requires that old schemas has been deleted -%% Returns ok | {error, Reason} -create_schema([]) -> - create_schema([node()]); -create_schema(Ns) when list(Ns) -> - case is_set(Ns) of - true -> - create_schema(Ns, mnesia_schema:ensure_no_schema(Ns)); - false -> - {error, {combine_error, Ns}} - end; -create_schema(Ns) -> - {error, {badarg, Ns}}. - -is_set(List) when list(List) -> - ordsets:is_set(lists:sort(List)); -is_set(_) -> - false. - -create_schema(Ns, ok) -> - %% Ensure that we access the intended Mnesia - %% directory. This function may not be called - %% during startup since it will cause the - %% application_controller to get into deadlock - case mnesia_lib:ensure_loaded(?APPLICATION) of - ok -> - case mnesia_monitor:get_env(schema_location) of - ram -> - {error, {has_no_disc, node()}}; - _ -> - case mnesia_schema:opt_create_dir(true, mnesia_lib:dir()) of - {error, What} -> - {error, What}; - ok -> - Mod = mnesia_backup, - Str = mk_str(), - File = mnesia_lib:dir(Str), - file:delete(File), - case catch make_initial_backup(Ns, File, Mod) of - {ok, _Res} -> - case do_install_fallback(File, Mod) of - ok -> - file:delete(File), - ok; - {error, Reason} -> - {error, Reason} - end; - {error, Reason} -> - {error, Reason} - end - end - end; - {error, Reason} -> - {error, Reason} - end; -create_schema(_Ns, {error, Reason}) -> - {error, Reason}; -create_schema(_Ns, Reason) -> - {error, Reason}. - -mk_str() -> - Now = [integer_to_list(I) || I <- tuple_to_list(now())], - lists:concat([node()] ++ Now ++ ".TMP"). - -make_initial_backup(Ns, Opaque, Mod) -> - Schema = [{schema, schema, mnesia_schema:get_initial_schema(disc_copies, Ns)}], - O2 = do_apply(Mod, open_write, [Opaque], Opaque), - O3 = do_apply(Mod, write, [O2, [mnesia_log:backup_log_header()]], O2), - O4 = do_apply(Mod, write, [O3, Schema], O3), - O5 = do_apply(Mod, commit_write, [O4], O4), - {ok, O5}. - -do_apply(_, write, [_, Items], Opaque) when Items == [] -> - Opaque; -do_apply(Mod, What, Args, _Opaque) -> - case catch apply(Mod, What, Args) of - {ok, Opaque2} -> Opaque2; - {error, Reason} -> throw({error, Reason}); - {'EXIT', Reason} -> throw({error, {'EXIT', Reason}}) - end. - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% Restore - -%% Restore schema and possibly other tables from a backup -%% and replicate them to the necessary nodes -%% Requires that old schemas has been deleted -%% Returns ok | {error, Reason} -install_fallback(Opaque) -> - install_fallback(Opaque, []). - -install_fallback(Opaque, Args) -> - %% Ensure that we access the intended Mnesia - %% directory. This function may not be called - %% during startup since it will cause the - %% application_controller to get into deadlock - case mnesia_lib:ensure_loaded(?APPLICATION) of - ok -> - do_install_fallback(Opaque, Args); - {error, Reason} -> - {error, Reason} - end. - -do_install_fallback(Opaque, Mod) when atom(Mod) -> - do_install_fallback(Opaque, [{module, Mod}]); -do_install_fallback(Opaque, Args) when list(Args) -> - case check_fallback_args(Args, #fallback_args{opaque = Opaque}) of - {ok, FA} -> - do_install_fallback(FA); - {error, Reason} -> - {error, Reason} - end; -do_install_fallback(_Opaque, Args) -> - {error, {badarg, Args}}. - -check_fallback_args([Arg | Tail], FA) -> - case catch check_fallback_arg_type(Arg, FA) of - {'EXIT', _Reason} -> - {error, {badarg, Arg}}; - FA2 -> - check_fallback_args(Tail, FA2) - end; -check_fallback_args([], FA) -> - {ok, FA}. - -check_fallback_arg_type(Arg, FA) -> - case Arg of - {scope, global} -> - FA#fallback_args{scope = global}; - {scope, local} -> - FA#fallback_args{scope = local}; - {module, Mod} -> - Mod2 = mnesia_monitor:do_check_type(backup_module, Mod), - FA#fallback_args{module = Mod2}; - {mnesia_dir, Dir} -> - FA#fallback_args{mnesia_dir = Dir, - use_default_dir = false}; - {keep_tables, Tabs} -> - atom_list(Tabs), - FA#fallback_args{keep_tables = Tabs}; - {skip_tables, Tabs} -> - atom_list(Tabs), - FA#fallback_args{skip_tables = Tabs}; - {default_op, keep_tables} -> - FA#fallback_args{default_op = keep_tables}; - {default_op, skip_tables} -> - FA#fallback_args{default_op = skip_tables} - end. - -atom_list([H | T]) when atom(H) -> - atom_list(T); -atom_list([]) -> - ok. - -do_install_fallback(FA) -> - Pid = spawn_link(?MODULE, install_fallback_master, [self(), FA]), - Res = - receive - {'EXIT', Pid, Reason} -> % if appl has trapped exit - {error, {'EXIT', Reason}}; - {Pid, Res2} -> - case Res2 of - {ok, _} -> - ok; - {error, Reason} -> - {error, {"Cannot install fallback", Reason}} - end - end, - Res. - -install_fallback_master(ClientPid, FA) -> - process_flag(trap_exit, true), - State = {start, FA}, - Opaque = FA#fallback_args.opaque, - Mod = FA#fallback_args.module, - Res = (catch iterate(Mod, fun restore_recs/4, Opaque, State)), - unlink(ClientPid), - ClientPid ! {self(), Res}, - exit(shutdown). - -restore_recs(_, _, _, stop) -> - throw({error, "restore_recs already stopped"}); - -restore_recs(Recs, Header, Schema, {start, FA}) -> - %% No records in backup - Schema2 = convert_schema(Header#log_header.log_version, Schema), - CreateList = lookup_schema(schema, Schema2), - case catch mnesia_schema:list2cs(CreateList) of - {'EXIT', Reason} -> - throw({error, {"Bad schema in restore_recs", Reason}}); - Cs -> - Ns = get_fallback_nodes(FA, Cs#cstruct.disc_copies), - global:set_lock({{mnesia_table_lock, schema}, self()}, Ns, infinity), - Args = [self(), FA], - Pids = [spawn_link(N, ?MODULE, fallback_receiver, Args) || N <- Ns], - send_fallback(Pids, {start, Header, Schema2}), - Res = restore_recs(Recs, Header, Schema2, Pids), - global:del_lock({{mnesia_table_lock, schema}, self()}, Ns), - Res - end; - -restore_recs([], _Header, _Schema, Pids) -> - send_fallback(Pids, swap), - send_fallback(Pids, stop), - stop; - -restore_recs(Recs, _, _, Pids) -> - send_fallback(Pids, {records, Recs}), - Pids. - -get_fallback_nodes(FA, Ns) -> - This = node(), - case lists:member(This, Ns) of - true -> - case FA#fallback_args.scope of - global -> Ns; - local -> [This] - end; - false -> - throw({error, {"No disc resident schema on local node", Ns}}) - end. - -send_fallback(Pids, Msg) when list(Pids), Pids /= [] -> - lists:foreach(fun(Pid) -> Pid ! {self(), Msg} end, Pids), - rec_answers(Pids, []). - -rec_answers([], Acc) -> - case {lists:keysearch(error, 1, Acc), mnesia_lib:uniq(Acc)} of - {{value, {error, Val}}, _} -> throw({error, Val}); - {_, [SameAnswer]} -> SameAnswer; - {_, Other} -> throw({error, {"Different answers", Other}}) - end; -rec_answers(Pids, Acc) -> - receive - {'EXIT', Pid, stopped} -> - Pids2 = lists:delete(Pid, Pids), - rec_answers(Pids2, [stopped|Acc]); - {'EXIT', Pid, Reason} -> - Pids2 = lists:delete(Pid, Pids), - rec_answers(Pids2, [{error, {'EXIT', Pid, Reason}}|Acc]); - {Pid, Reply} -> - Pids2 = lists:delete(Pid, Pids), - rec_answers(Pids2, [Reply|Acc]) - end. - -fallback_exists() -> - Fname = fallback_bup(), - fallback_exists(Fname). - -fallback_exists(Fname) -> - case mnesia_monitor:use_dir() of - true -> - mnesia_lib:exists(Fname); - false -> - case ?catch_val(active_fallback) of - {'EXIT', _} -> false; - Bool -> Bool - end - end. - -fallback_name() -> "FALLBACK.BUP". -fallback_bup() -> mnesia_lib:dir(fallback_name()). - -fallback_tmp_name() -> "FALLBACK.TMP". -%% fallback_full_tmp_name() -> mnesia_lib:dir(fallback_tmp_name()). - -fallback_receiver(Master, FA) -> - process_flag(trap_exit, true), - - case catch register(mnesia_fallback, self()) of - {'EXIT', _} -> - Reason = {already_exists, node()}, - local_fallback_error(Master, Reason); - true -> - FA2 = check_fallback_dir(Master, FA), - Bup = FA2#fallback_args.fallback_bup, - case mnesia_lib:exists(Bup) of - true -> - Reason2 = {already_exists, node()}, - local_fallback_error(Master, Reason2); - false -> - Mod = mnesia_backup, - Tmp = FA2#fallback_args.fallback_tmp, - R = #restore{mode = replace, - bup_module = Mod, - bup_data = Tmp}, - file:delete(Tmp), - case catch fallback_receiver_loop(Master, R, FA2, schema) of - {error, Reason} -> - local_fallback_error(Master, Reason); - Other -> - exit(Other) - end - end - end. - -local_fallback_error(Master, Reason) -> - Master ! {self(), {error, Reason}}, - unlink(Master), - exit(Reason). - -check_fallback_dir(Master, FA) -> - case mnesia:system_info(schema_location) of - ram -> - Reason = {has_no_disc, node()}, - local_fallback_error(Master, Reason); - _ -> - Dir = check_fallback_dir_arg(Master, FA), - Bup = filename:join([Dir, fallback_name()]), - Tmp = filename:join([Dir, fallback_tmp_name()]), - FA#fallback_args{fallback_bup = Bup, - fallback_tmp = Tmp, - mnesia_dir = Dir} - end. - -check_fallback_dir_arg(Master, FA) -> - case FA#fallback_args.use_default_dir of - true -> - mnesia_lib:dir(); - false when FA#fallback_args.scope == local -> - Dir = FA#fallback_args.mnesia_dir, - case catch mnesia_monitor:do_check_type(dir, Dir) of - {'EXIT', _R} -> - Reason = {badarg, {dir, Dir}, node()}, - local_fallback_error(Master, Reason); - AbsDir-> - AbsDir - end; - false when FA#fallback_args.scope == global -> - Reason = {combine_error, global, dir, node()}, - local_fallback_error(Master, Reason) - end. - -fallback_receiver_loop(Master, R, FA, State) -> - receive - {Master, {start, Header, Schema}} when State == schema -> - Dir = FA#fallback_args.mnesia_dir, - throw_bad_res(ok, mnesia_schema:opt_create_dir(true, Dir)), - R2 = safe_apply(R, open_write, [R#restore.bup_data]), - R3 = safe_apply(R2, write, [R2#restore.bup_data, [Header]]), - BupSchema = [schema2bup(S) || S <- Schema], - R4 = safe_apply(R3, write, [R3#restore.bup_data, BupSchema]), - Master ! {self(), ok}, - fallback_receiver_loop(Master, R4, FA, records); - - {Master, {records, Recs}} when State == records -> - R2 = safe_apply(R, write, [R#restore.bup_data, Recs]), - Master ! {self(), ok}, - fallback_receiver_loop(Master, R2, FA, records); - - {Master, swap} when State /= schema -> - ?eval_debug_fun({?MODULE, fallback_receiver_loop, pre_swap}, []), - safe_apply(R, commit_write, [R#restore.bup_data]), - Bup = FA#fallback_args.fallback_bup, - Tmp = FA#fallback_args.fallback_tmp, - throw_bad_res(ok, file:rename(Tmp, Bup)), - catch mnesia_lib:set(active_fallback, true), - ?eval_debug_fun({?MODULE, fallback_receiver_loop, post_swap}, []), - Master ! {self(), ok}, - fallback_receiver_loop(Master, R, FA, stop); - - {Master, stop} when State == stop -> - stopped; - - Msg -> - safe_apply(R, abort_write, [R#restore.bup_data]), - Tmp = FA#fallback_args.fallback_tmp, - file:delete(Tmp), - throw({error, "Unexpected msg fallback_receiver_loop", Msg}) - end. - -throw_bad_res(Expected, Expected) -> Expected; -throw_bad_res(_Expected, {error, Actual}) -> throw({error, Actual}); -throw_bad_res(_Expected, Actual) -> throw({error, Actual}). - --record(local_tab, {name, storage_type, dets_args, open, close, add, record_name}). - -tm_fallback_start(IgnoreFallback) -> - mnesia_schema:lock_schema(), - Res = do_fallback_start(fallback_exists(), IgnoreFallback), - mnesia_schema: unlock_schema(), - case Res of - ok -> ok; - {error, Reason} -> exit(Reason) - end. - -do_fallback_start(false, _IgnoreFallback) -> - ok; -do_fallback_start(true, true) -> - verbose("Ignoring fallback at startup, but leaving it active...~n", []), - mnesia_lib:set(active_fallback, true), - ok; -do_fallback_start(true, false) -> - verbose("Starting from fallback...~n", []), - - Fname = fallback_bup(), - Mod = mnesia_backup, - Ets = ?ets_new_table(mnesia_local_tables, [set, public, {keypos, 2}]), - case catch iterate(Mod, fun restore_tables/4, Fname, {start, Ets}) of - {ok, Res} -> - case Res of - {local, _, LT} -> %% Close the last file - (LT#local_tab.close)(LT); - _ -> - ignore - end, - List = ?ets_match_object(Ets, '_'), - Tabs = [L#local_tab.name || L <- List, L#local_tab.name /= schema], - ?ets_delete_table(Ets), - mnesia_lib:swap_tmp_files(Tabs), - catch dets:close(schema), - Tmp = mnesia_lib:tab2tmp(schema), - Dat = mnesia_lib:tab2dat(schema), - case file:rename(Tmp, Dat) of - ok -> - file:delete(Fname), - ok; - {error, Reason} -> - file:delete(Tmp), - {error, {"Cannot start from fallback. Rename error.", Reason}} - end; - {error, Reason} -> - {error, {"Cannot start from fallback", Reason}}; - {'EXIT', Reason} -> - {error, {"Cannot start from fallback", Reason}} - end. - -restore_tables(Recs, Header, Schema, {start, LocalTabs}) -> - Dir = mnesia_lib:dir(), - OldDir = filename:join([Dir, "OLD_DIR"]), - mnesia_schema:purge_dir(OldDir, []), - mnesia_schema:purge_dir(Dir, [fallback_name()]), - init_dat_files(Schema, LocalTabs), - State = {new, LocalTabs}, - restore_tables(Recs, Header, Schema, State); -restore_tables([Rec | Recs], Header, Schema, {new, LocalTabs}) -> - Tab = element(1, Rec), - case ?ets_lookup(LocalTabs, Tab) of - [] -> - State = {not_local, LocalTabs, Tab}, - restore_tables(Recs, Header, Schema, State); - [L] when record(L, local_tab) -> - (L#local_tab.open)(Tab, L), - State = {local, LocalTabs, L}, - restore_tables([Rec | Recs], Header, Schema, State) - end; -restore_tables([Rec | Recs], Header, Schema, S = {not_local, LocalTabs, PrevTab}) -> - Tab = element(1, Rec), - if - Tab == PrevTab -> - restore_tables(Recs, Header, Schema, S); - true -> - State = {new, LocalTabs}, - restore_tables([Rec | Recs], Header, Schema, State) - end; -restore_tables([Rec | Recs], Header, Schema, State = {local, LocalTabs, L}) -> - Tab = element(1, Rec), - if - Tab == L#local_tab.name -> - Key = element(2, Rec), - (L#local_tab.add)(Tab, Key, Rec, L), - restore_tables(Recs, Header, Schema, State); - true -> - (L#local_tab.close)(L), - NState = {new, LocalTabs}, - restore_tables([Rec | Recs], Header, Schema, NState) - end; -restore_tables([], _Header, _Schema, State) -> - State. - -%% Creates all neccessary dat files and inserts -%% the table definitions in the schema table -%% -%% Returns a list of local_tab tuples for all local tables -init_dat_files(Schema, LocalTabs) -> - Fname = mnesia_lib:tab2tmp(schema), - Args = [{file, Fname}, {keypos, 2}, {type, set}], - case dets:open_file(schema, Args) of % Assume schema lock - {ok, _} -> - create_dat_files(Schema, LocalTabs), - dets:close(schema), - LocalTab = #local_tab{name = schema, - storage_type = disc_copies, - dets_args = Args, - open = fun open_media/2, - close = fun close_media/1, - add = fun add_to_media/4, - record_name = schema}, - ?ets_insert(LocalTabs, LocalTab); - {error, Reason} -> - throw({error, {"Cannot open file", schema, Args, Reason}}) - end. - -create_dat_files([{schema, schema, TabDef} | Tail], LocalTabs) -> - ok = dets:insert(schema, {schema, schema, TabDef}), - create_dat_files(Tail, LocalTabs); -create_dat_files([{schema, Tab, TabDef} | Tail], LocalTabs) -> - Cs = mnesia_schema:list2cs(TabDef), - ok = dets:insert(schema, {schema, Tab, TabDef}), - RecName = Cs#cstruct.record_name, - case mnesia_lib:cs_to_storage_type(node(), Cs) of - unknown -> - cleanup_dat_file(Tab), - create_dat_files(Tail, LocalTabs); - disc_only_copies -> - Fname = mnesia_lib:tab2tmp(Tab), - Args = [{file, Fname}, {keypos, 2}, - {type, mnesia_lib:disk_type(Tab, Cs#cstruct.type)}], - case mnesia_lib:dets_sync_open(Tab, Args) of - {ok, _} -> - mnesia_lib:dets_sync_close(Tab), - LocalTab = #local_tab{name = Tab, - storage_type = disc_only_copies, - dets_args = Args, - open = fun open_media/2, - close = fun close_media/1, - add = fun add_to_media/4, - record_name = RecName}, - ?ets_insert(LocalTabs, LocalTab), - create_dat_files(Tail, LocalTabs); - {error, Reason} -> - throw({error, {"Cannot open file", Tab, Args, Reason}}) - end; - ram_copies -> - %% Create .DCD if needed in open_media in case any ram_copies - %% are backed up. - LocalTab = #local_tab{name = Tab, - storage_type = ram_copies, - dets_args = ignore, - open = fun open_media/2, - close = fun close_media/1, - add = fun add_to_media/4, - record_name = RecName}, - ?ets_insert(LocalTabs, LocalTab), - create_dat_files(Tail, LocalTabs); - Storage -> - %% Create DCD - Fname = mnesia_lib:tab2dcd(Tab), - file:delete(Fname), - Log = mnesia_log:open_log(fallback_tab, mnesia_log:dcd_log_header(), - Fname, false), - LocalTab = #local_tab{name = Tab, - storage_type = Storage, - dets_args = ignore, - open = fun open_media/2, - close = fun close_media/1, - add = fun add_to_media/4, - record_name = RecName}, - mnesia_log:close_log(Log), - ?ets_insert(LocalTabs, LocalTab), - create_dat_files(Tail, LocalTabs) - end; -create_dat_files([{schema, Tab} | Tail], LocalTabs) -> - cleanup_dat_file(Tab), - create_dat_files(Tail, LocalTabs); -create_dat_files([], _LocalTabs) -> - ok. - -cleanup_dat_file(Tab) -> - ok = dets:delete(schema, {schema, Tab}), - mnesia_lib:cleanup_tmp_files([Tab]). - -open_media(Tab, LT) -> - case LT#local_tab.storage_type of - disc_only_copies -> - Args = LT#local_tab.dets_args, - case mnesia_lib:dets_sync_open(Tab, Args) of - {ok, _} -> ok; - {error, Reason} -> - throw({error, {"Cannot open file", Tab, Args, Reason}}) - end; - ram_copies -> - %% Create .DCD as ram_copies backed up. - FnameDCD = mnesia_lib:tab2dcd(Tab), - file:delete(FnameDCD), - Log = mnesia_log:open_log(fallback_tab, - mnesia_log:dcd_log_header(), - FnameDCD, false), - mnesia_log:close_log(Log), - - %% Create .DCL - Fname = mnesia_lib:tab2dcl(Tab), - file:delete(Fname), - mnesia_log:open_log({?MODULE,Tab}, - mnesia_log:dcl_log_header(), - Fname, false, false, - read_write); - _ -> - Fname = mnesia_lib:tab2dcl(Tab), - file:delete(Fname), - mnesia_log:open_log({?MODULE,Tab}, - mnesia_log:dcl_log_header(), - Fname, false, false, - read_write) - end. -close_media(L) -> - Tab = L#local_tab.name, - case L#local_tab.storage_type of - disc_only_copies -> - mnesia_lib:dets_sync_close(Tab); - _ -> - mnesia_log:close_log({?MODULE,Tab}) - end. - -add_to_media(Tab, Key, Rec, L) -> - RecName = L#local_tab.record_name, - case L#local_tab.storage_type of - disc_only_copies -> - case Rec of - {Tab, Key} -> - ok = dets:delete(Tab, Key); - (Rec) when Tab == RecName -> - ok = dets:insert(Tab, Rec); - (Rec) -> - Rec2 = setelement(1, Rec, RecName), - ok = dets:insert(Tab, Rec2) - end; - _ -> - Log = {?MODULE, Tab}, - case Rec of - {Tab, Key} -> - mnesia_log:append(Log, {{Tab, Key}, {Tab, Key}, delete}); - (Rec) when Tab == RecName -> - mnesia_log:append(Log, {{Tab, Key}, Rec, write}); - (Rec) -> - Rec2 = setelement(1, Rec, RecName), - mnesia_log:append(Log, {{Tab, Key}, Rec2, write}) - end - end. - -uninstall_fallback() -> - uninstall_fallback([{scope, global}]). - -uninstall_fallback(Args) -> - case check_fallback_args(Args, #fallback_args{}) of - {ok, FA} -> - do_uninstall_fallback(FA); - {error, Reason} -> - {error, Reason} - end. - -do_uninstall_fallback(FA) -> - %% Ensure that we access the intended Mnesia - %% directory. This function may not be called - %% during startup since it will cause the - %% application_controller to get into deadlock - case mnesia_lib:ensure_loaded(?APPLICATION) of - ok -> - Pid = spawn_link(?MODULE, uninstall_fallback_master, [self(), FA]), - receive - {'EXIT', Pid, Reason} -> % if appl has trapped exit - {error, {'EXIT', Reason}}; - {Pid, Res} -> - Res - end; - {error, Reason} -> - {error, Reason} - end. - -uninstall_fallback_master(ClientPid, FA) -> - process_flag(trap_exit, true), - - FA2 = check_fallback_dir(ClientPid, FA), % May exit - Bup = FA2#fallback_args.fallback_bup, - case fallback_to_schema(Bup) of - {ok, fallback, List} -> - Cs = mnesia_schema:list2cs(List), - case catch get_fallback_nodes(FA, Cs#cstruct.disc_copies) of - Ns when list(Ns) -> - do_uninstall(ClientPid, Ns, FA); - {error, Reason} -> - local_fallback_error(ClientPid, Reason) - end; - {error, Reason} -> - local_fallback_error(ClientPid, Reason) - end. - -do_uninstall(ClientPid, Ns, FA) -> - Args = [self(), FA], - global:set_lock({{mnesia_table_lock, schema}, self()}, Ns, infinity), - Pids = [spawn_link(N, ?MODULE, local_uninstall_fallback, Args) || N <- Ns], - Res = do_uninstall(ClientPid, Pids, [], [], ok), - global:del_lock({{mnesia_table_lock, schema}, self()}, Ns), - ClientPid ! {self(), Res}, - unlink(ClientPid), - exit(shutdown). - -do_uninstall(ClientPid, [Pid | Pids], GoodPids, BadNodes, Res) -> - receive - %% {'EXIT', ClientPid, _} -> - %% client_exit; - {'EXIT', Pid, Reason} -> - BadNode = node(Pid), - BadRes = {error, {"Uninstall fallback", BadNode, Reason}}, - do_uninstall(ClientPid, Pids, GoodPids, [BadNode | BadNodes], BadRes); - {Pid, {error, Reason}} -> - BadNode = node(Pid), - BadRes = {error, {"Uninstall fallback", BadNode, Reason}}, - do_uninstall(ClientPid, Pids, GoodPids, [BadNode | BadNodes], BadRes); - {Pid, started} -> - do_uninstall(ClientPid, Pids, [Pid | GoodPids], BadNodes, Res) - end; -do_uninstall(ClientPid, [], GoodPids, [], ok) -> - lists:foreach(fun(Pid) -> Pid ! {self(), do_uninstall} end, GoodPids), - rec_uninstall(ClientPid, GoodPids, ok); -do_uninstall(_ClientPid, [], GoodPids, BadNodes, BadRes) -> - lists:foreach(fun(Pid) -> exit(Pid, shutdown) end, GoodPids), - {error, {node_not_running, BadNodes, BadRes}}. - -local_uninstall_fallback(Master, FA) -> - %% Don't trap exit - - register(mnesia_fallback, self()), % May exit - FA2 = check_fallback_dir(Master, FA), % May exit - Master ! {self(), started}, - - receive - {Master, do_uninstall} -> - ?eval_debug_fun({?MODULE, uninstall_fallback2, pre_delete}, []), - catch mnesia_lib:set(active_fallback, false), - Tmp = FA2#fallback_args.fallback_tmp, - Bup = FA2#fallback_args.fallback_bup, - file:delete(Tmp), - Res = - case fallback_exists(Bup) of - true -> file:delete(Bup); - false -> ok - end, - ?eval_debug_fun({?MODULE, uninstall_fallback2, post_delete}, []), - Master ! {self(), Res}, - unlink(Master), - exit(normal) - end. - -rec_uninstall(ClientPid, [Pid | Pids], AccRes) -> - receive - %% {'EXIT', ClientPid, _} -> - %% exit(shutdown); - {'EXIT', Pid, R} -> - Reason = {node_not_running, {node(Pid), R}}, - rec_uninstall(ClientPid, Pids, {error, Reason}); - {Pid, ok} -> - rec_uninstall(ClientPid, Pids, AccRes); - {Pid, BadRes} -> - rec_uninstall(ClientPid, Pids, BadRes) - end; -rec_uninstall(ClientPid, [], Res) -> - ClientPid ! {self(), Res}, - unlink(ClientPid), - exit(normal). - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% Backup traversal - -%% Iterate over a backup and produce a new backup. -%% Fun(BackupItem, Acc) is applied for each BackupItem. -%% -%% Valid BackupItems are: -%% -%% {schema, Tab} Table to be deleted -%% {schema, Tab, CreateList} Table to be created, CreateList may be empty -%% {schema, db_nodes, DbNodes}List of nodes, defaults to [node()] OLD -%% {schema, version, Version} Schema version OLD -%% {schema, cookie, Cookie} Unique schema cookie OLD -%% {Tab, Key} Oid for record to be deleted -%% Record Record to be inserted. -%% -%% The Fun must return a tuple {BackupItems, NewAcc} -%% where BackupItems is a list of valid BackupItems and -%% NewAcc is a new accumulator value. Once BackupItems -%% that not are schema related has been returned, no more schema -%% items may be returned. The schema related items must always be -%% first in the backup. -%% -%% If TargetMod == read_only, no new backup will be created. -%% -%% Opening of the source media will be performed by -%% to SourceMod:open_read(Source) -%% -%% Opening of the target media will be performed by -%% to TargetMod:open_write(Target) -traverse_backup(Source, Target, Fun, Acc) -> - Mod = mnesia_monitor:get_env(backup_module), - traverse_backup(Source, Mod, Target, Mod, Fun, Acc). - -traverse_backup(Source, SourceMod, Target, TargetMod, Fun, Acc) -> - Args = [self(), Source, SourceMod, Target, TargetMod, Fun, Acc], - Pid = spawn_link(?MODULE, do_traverse_backup, Args), - receive - {'EXIT', Pid, Reason} -> - {error, {"Backup traversal crashed", Reason}}; - {iter_done, Pid, Res} -> - Res - end. - -do_traverse_backup(ClientPid, Source, SourceMod, Target, TargetMod, Fun, Acc) -> - process_flag(trap_exit, true), - Iter = - if - TargetMod /= read_only -> - case catch do_apply(TargetMod, open_write, [Target], Target) of - {error, Error} -> - unlink(ClientPid), - ClientPid ! {iter_done, self(), {error, Error}}, - exit(Error); - Else -> Else - end; - true -> - ignore - end, - A = {start, Fun, Acc, TargetMod, Iter}, - Res = - case iterate(SourceMod, fun trav_apply/4, Source, A) of - {ok, {iter, _, Acc2, _, Iter2}} when TargetMod /= read_only -> - case catch do_apply(TargetMod, commit_write, [Iter2], Iter2) of - {error, Reason} -> - {error, Reason}; - _ -> - {ok, Acc2} - end; - {ok, {iter, _, Acc2, _, _}} -> - {ok, Acc2}; - {error, Reason} when TargetMod /= read_only-> - catch do_apply(TargetMod, abort_write, [Iter], Iter), - {error, {"Backup traversal failed", Reason}}; - {error, Reason} -> - {error, {"Backup traversal failed", Reason}} - end, - unlink(ClientPid), - ClientPid ! {iter_done, self(), Res}. - -trav_apply(Recs, _Header, _Schema, {iter, Fun, Acc, Mod, Iter}) -> - {NewRecs, Acc2} = filter_foldl(Fun, Acc, Recs), - if - Mod /= read_only, NewRecs /= [] -> - Iter2 = do_apply(Mod, write, [Iter, NewRecs], Iter), - {iter, Fun, Acc2, Mod, Iter2}; - true -> - {iter, Fun, Acc2, Mod, Iter} - end; -trav_apply(Recs, Header, Schema, {start, Fun, Acc, Mod, Iter}) -> - Iter2 = - if - Mod /= read_only -> - do_apply(Mod, write, [Iter, [Header]], Iter); - true -> - Iter - end, - TravAcc = trav_apply(Schema, Header, Schema, {iter, Fun, Acc, Mod, Iter2}), - trav_apply(Recs, Header, Schema, TravAcc). - -filter_foldl(Fun, Acc, [Head|Tail]) -> - case Fun(Head, Acc) of - {HeadItems, HeadAcc} when list(HeadItems) -> - {TailItems, TailAcc} = filter_foldl(Fun, HeadAcc, Tail), - {HeadItems ++ TailItems, TailAcc}; - Other -> - throw({error, {"Fun must return a list", Other}}) - end; -filter_foldl(_Fun, Acc, []) -> - {[], Acc}. - |