|
8 | 8 | #include "errcode.h"
|
9 | 9 | #include "error.h"
|
10 | 10 | #include "fs.h"
|
| 11 | +#include "recovery_passes.h" |
11 | 12 | #include "snapshot.h"
|
12 | 13 |
|
13 | 14 | #include <linux/random.h>
|
@@ -574,6 +575,13 @@ static int check_snapshot_tree(struct btree_trans *trans,
|
574 | 575 | u32 subvol_id;
|
575 | 576 |
|
576 | 577 | ret = bch2_snapshot_tree_master_subvol(trans, root_id, &subvol_id);
|
| 578 | + bch_err_fn(c, ret); |
| 579 | + |
| 580 | + if (bch2_err_matches(ret, ENOENT)) { /* nothing to be done here */ |
| 581 | + ret = 0; |
| 582 | + goto err; |
| 583 | + } |
| 584 | + |
577 | 585 | if (ret)
|
578 | 586 | goto err;
|
579 | 587 |
|
@@ -731,7 +739,6 @@ static int check_snapshot(struct btree_trans *trans,
|
731 | 739 | u32 parent_id = bch2_snapshot_parent_early(c, k.k->p.offset);
|
732 | 740 | u32 real_depth;
|
733 | 741 | struct printbuf buf = PRINTBUF;
|
734 |
| - bool should_have_subvol; |
735 | 742 | u32 i, id;
|
736 | 743 | int ret = 0;
|
737 | 744 |
|
@@ -777,7 +784,7 @@ static int check_snapshot(struct btree_trans *trans,
|
777 | 784 | }
|
778 | 785 | }
|
779 | 786 |
|
780 |
| - should_have_subvol = BCH_SNAPSHOT_SUBVOL(&s) && |
| 787 | + bool should_have_subvol = BCH_SNAPSHOT_SUBVOL(&s) && |
781 | 788 | !BCH_SNAPSHOT_DELETED(&s);
|
782 | 789 |
|
783 | 790 | if (should_have_subvol) {
|
@@ -879,6 +886,154 @@ int bch2_check_snapshots(struct bch_fs *c)
|
879 | 886 | return ret;
|
880 | 887 | }
|
881 | 888 |
|
| 889 | +static int check_snapshot_exists(struct btree_trans *trans, u32 id) |
| 890 | +{ |
| 891 | + struct bch_fs *c = trans->c; |
| 892 | + |
| 893 | + if (bch2_snapshot_equiv(c, id)) |
| 894 | + return 0; |
| 895 | + |
| 896 | + u32 tree_id; |
| 897 | + int ret = bch2_snapshot_tree_create(trans, id, 0, &tree_id); |
| 898 | + if (ret) |
| 899 | + return ret; |
| 900 | + |
| 901 | + struct bkey_i_snapshot *snapshot = bch2_trans_kmalloc(trans, sizeof(*snapshot)); |
| 902 | + ret = PTR_ERR_OR_ZERO(snapshot); |
| 903 | + if (ret) |
| 904 | + return ret; |
| 905 | + |
| 906 | + bkey_snapshot_init(&snapshot->k_i); |
| 907 | + snapshot->k.p = POS(0, id); |
| 908 | + snapshot->v.tree = cpu_to_le32(tree_id); |
| 909 | + snapshot->v.btime.lo = cpu_to_le64(bch2_current_time(c)); |
| 910 | + |
| 911 | + return bch2_btree_insert_trans(trans, BTREE_ID_snapshots, &snapshot->k_i, 0) ?: |
| 912 | + bch2_mark_snapshot(trans, BTREE_ID_snapshots, 0, |
| 913 | + bkey_s_c_null, bkey_i_to_s(&snapshot->k_i), 0) ?: |
| 914 | + bch2_snapshot_set_equiv(trans, bkey_i_to_s_c(&snapshot->k_i)); |
| 915 | +} |
| 916 | + |
| 917 | +/* Figure out which snapshot nodes belong in the same tree: */ |
| 918 | +struct snapshot_tree_reconstruct { |
| 919 | + enum btree_id btree; |
| 920 | + struct bpos cur_pos; |
| 921 | + snapshot_id_list cur_ids; |
| 922 | + DARRAY(snapshot_id_list) trees; |
| 923 | +}; |
| 924 | + |
| 925 | +static void snapshot_tree_reconstruct_exit(struct snapshot_tree_reconstruct *r) |
| 926 | +{ |
| 927 | + darray_for_each(r->trees, i) |
| 928 | + darray_exit(i); |
| 929 | + darray_exit(&r->trees); |
| 930 | + darray_exit(&r->cur_ids); |
| 931 | +} |
| 932 | + |
| 933 | +static inline bool same_snapshot(struct snapshot_tree_reconstruct *r, struct bpos pos) |
| 934 | +{ |
| 935 | + return r->btree == BTREE_ID_inodes |
| 936 | + ? r->cur_pos.offset == pos.offset |
| 937 | + : r->cur_pos.inode == pos.inode; |
| 938 | +} |
| 939 | + |
| 940 | +static inline bool snapshot_id_lists_have_common(snapshot_id_list *l, snapshot_id_list *r) |
| 941 | +{ |
| 942 | + darray_for_each(*l, i) |
| 943 | + if (snapshot_list_has_id(r, *i)) |
| 944 | + return true; |
| 945 | + return false; |
| 946 | +} |
| 947 | + |
| 948 | +static void snapshot_id_list_to_text(struct printbuf *out, snapshot_id_list *s) |
| 949 | +{ |
| 950 | + bool first = true; |
| 951 | + darray_for_each(*s, i) { |
| 952 | + if (!first) |
| 953 | + prt_char(out, ' '); |
| 954 | + first = false; |
| 955 | + prt_printf(out, "%u", *i); |
| 956 | + } |
| 957 | +} |
| 958 | + |
| 959 | +static int snapshot_tree_reconstruct_next(struct bch_fs *c, struct snapshot_tree_reconstruct *r) |
| 960 | +{ |
| 961 | + if (r->cur_ids.nr) { |
| 962 | + darray_for_each(r->trees, i) |
| 963 | + if (snapshot_id_lists_have_common(i, &r->cur_ids)) { |
| 964 | + int ret = snapshot_list_merge(c, i, &r->cur_ids); |
| 965 | + if (ret) |
| 966 | + return ret; |
| 967 | + goto out; |
| 968 | + } |
| 969 | + darray_push(&r->trees, r->cur_ids); |
| 970 | + darray_init(&r->cur_ids); |
| 971 | + } |
| 972 | +out: |
| 973 | + r->cur_ids.nr = 0; |
| 974 | + return 0; |
| 975 | +} |
| 976 | + |
| 977 | +static int get_snapshot_trees(struct bch_fs *c, struct snapshot_tree_reconstruct *r, struct bpos pos) |
| 978 | +{ |
| 979 | + if (!same_snapshot(r, pos)) |
| 980 | + snapshot_tree_reconstruct_next(c, r); |
| 981 | + r->cur_pos = pos; |
| 982 | + return snapshot_list_add_nodup(c, &r->cur_ids, pos.snapshot); |
| 983 | +} |
| 984 | + |
| 985 | +int bch2_reconstruct_snapshots(struct bch_fs *c) |
| 986 | +{ |
| 987 | + struct btree_trans *trans = bch2_trans_get(c); |
| 988 | + struct printbuf buf = PRINTBUF; |
| 989 | + struct snapshot_tree_reconstruct r = {}; |
| 990 | + int ret = 0; |
| 991 | + |
| 992 | + for (unsigned btree = 0; btree < BTREE_ID_NR; btree++) { |
| 993 | + if (btree_type_has_snapshots(btree)) { |
| 994 | + r.btree = btree; |
| 995 | + |
| 996 | + ret = for_each_btree_key(trans, iter, btree, POS_MIN, |
| 997 | + BTREE_ITER_ALL_SNAPSHOTS|BTREE_ITER_PREFETCH, k, ({ |
| 998 | + get_snapshot_trees(c, &r, k.k->p); |
| 999 | + })); |
| 1000 | + if (ret) |
| 1001 | + goto err; |
| 1002 | + |
| 1003 | + snapshot_tree_reconstruct_next(c, &r); |
| 1004 | + } |
| 1005 | + } |
| 1006 | + |
| 1007 | + darray_for_each(r.trees, t) { |
| 1008 | + printbuf_reset(&buf); |
| 1009 | + snapshot_id_list_to_text(&buf, t); |
| 1010 | + |
| 1011 | + darray_for_each(*t, id) { |
| 1012 | + if (fsck_err_on(!bch2_snapshot_equiv(c, *id), |
| 1013 | + c, snapshot_node_missing, |
| 1014 | + "snapshot node %u from tree %s missing", *id, buf.buf)) { |
| 1015 | + if (t->nr > 1) { |
| 1016 | + bch_err(c, "cannot reconstruct snapshot trees with multiple nodes"); |
| 1017 | + ret = -BCH_ERR_fsck_repair_unimplemented; |
| 1018 | + goto err; |
| 1019 | + } |
| 1020 | + |
| 1021 | + ret = commit_do(trans, NULL, NULL, BCH_TRANS_COMMIT_no_enospc, |
| 1022 | + check_snapshot_exists(trans, *id)); |
| 1023 | + if (ret) |
| 1024 | + goto err; |
| 1025 | + } |
| 1026 | + } |
| 1027 | + } |
| 1028 | +fsck_err: |
| 1029 | +err: |
| 1030 | + bch2_trans_put(trans); |
| 1031 | + snapshot_tree_reconstruct_exit(&r); |
| 1032 | + printbuf_exit(&buf); |
| 1033 | + bch_err_fn(c, ret); |
| 1034 | + return ret; |
| 1035 | +} |
| 1036 | + |
882 | 1037 | /*
|
883 | 1038 | * Mark a snapshot as deleted, for future cleanup:
|
884 | 1039 | */
|
@@ -1689,6 +1844,20 @@ int bch2_snapshots_read(struct bch_fs *c)
|
1689 | 1844 | POS_MIN, 0, k,
|
1690 | 1845 | (set_is_ancestor_bitmap(c, k.k->p.offset), 0)));
|
1691 | 1846 | bch_err_fn(c, ret);
|
| 1847 | + |
| 1848 | + /* |
| 1849 | + * It's important that we check if we need to reconstruct snapshots |
| 1850 | + * before going RW, so we mark that pass as required in the superblock - |
| 1851 | + * otherwise, we could end up deleting keys with missing snapshot nodes |
| 1852 | + * instead |
| 1853 | + */ |
| 1854 | + BUG_ON(!test_bit(BCH_FS_new_fs, &c->flags) && |
| 1855 | + test_bit(BCH_FS_may_go_rw, &c->flags)); |
| 1856 | + |
| 1857 | + if (bch2_err_matches(ret, EIO) || |
| 1858 | + (c->sb.btrees_lost_data & BIT_ULL(BTREE_ID_snapshots))) |
| 1859 | + ret = bch2_run_explicit_recovery_pass_persistent(c, BCH_RECOVERY_PASS_reconstruct_snapshots); |
| 1860 | + |
1692 | 1861 | return ret;
|
1693 | 1862 | }
|
1694 | 1863 |
|
|
0 commit comments