1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
|
#include <fcntl.h>
#include <getopt.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "cmds.h"
#include "libbcachefs.h"
#include "tools-util.h"
#include "libbcachefs/bcachefs.h"
#include "libbcachefs/btree_iter.h"
#include "libbcachefs/errcode.h"
#include "libbcachefs/error.h"
#include "libbcachefs/sb-members.h"
#include "libbcachefs/super.h"
static void kill_btree_node_usage(void)
{
puts("bcachefs kill_btree_node - make btree nodes unreadable\n"
"Usage: bcachefs kill_btree_node [OPTION]... <devices>\n"
"\n"
"Options:\n"
" -n, --node btree:level:idx Node to kill\n"
" -d, --dev dev Device index (default: kill all replicas)\n"
" -h Display this help and exit\n"
"Report bugs to <linux-bcachefs@vger.kernel.org>");
}
struct kill_node {
unsigned btree;
unsigned level;
u64 idx;
};
int cmd_kill_btree_node(int argc, char *argv[])
{
static const struct option longopts[] = {
{ "node", required_argument, NULL, 'n' },
{ "dev", required_argument, NULL, 'd' },
{ "help", no_argument, NULL, 'h' },
{ NULL }
};
struct bch_opts opts = bch2_opts_empty();
DARRAY(struct kill_node) kill_nodes = {};
int opt, dev_idx = -1;
opt_set(opts, read_only, true);
while ((opt = getopt_long(argc, argv, "n:d:h", longopts, NULL)) != -1)
switch (opt) {
case 'n': {
char *p = optarg;
const char *str_btree = strsep(&p, ":");
const char *str_level = strsep(&p, ":");
const char *str_idx = strsep(&p, ":");
struct kill_node n = {
.btree = read_string_list_or_die(str_btree,
__bch2_btree_ids, "btree id"),
};
if (str_level &&
(kstrtouint(str_level, 10, &n.level) || n.level >= BTREE_MAX_DEPTH))
die("invalid level");
if (str_idx &&
kstrtoull(str_idx, 10, &n.idx))
die("invalid index %s", str_idx);
darray_push(&kill_nodes, n);
break;
}
case 'd':
if (kstrtoint(optarg, 10, &dev_idx))
die("invalid device index %s", optarg);
break;
case 'h':
kill_btree_node_usage();
exit(EXIT_SUCCESS);
}
args_shift(optind);
if (!argc)
die("Please supply device(s)");
darray_const_str devs = get_or_split_cmdline_devs(argc, argv);
struct bch_fs *c = bch2_fs_open(&devs, &opts);
if (IS_ERR(c))
die("error opening %s: %s", argv[0], bch2_err_str(PTR_ERR(c)));
int ret;
void *zeroes;
ret = posix_memalign(&zeroes, c->opts.block_size, c->opts.block_size);
if (ret)
die("error %s from posix_memalign", bch2_err_str(ret));
struct btree_trans *trans = bch2_trans_get(c);
darray_for_each(kill_nodes, i) {
ret = __for_each_btree_node(trans, iter, i->btree, POS_MIN, 0, i->level, 0, b, ({
if (b->c.level != i->level)
continue;
int ret2 = 0;
if (!i->idx) {
ret2 = 1;
struct bkey_ptrs_c ptrs = bch2_bkey_ptrs_c(bkey_i_to_s_c(&b->key));
bkey_for_each_ptr(ptrs, ptr) {
if (dev_idx >= 0 && ptr->dev != dev_idx)
continue;
struct bch_dev *ca = bch2_dev_tryget(c, ptr->dev);
if (!ca)
continue;
struct printbuf buf = PRINTBUF;
bch2_bkey_val_to_text(&buf, c, bkey_i_to_s_c(&b->key));
bch_info(c, "killing btree node on dev %i %s l=%u\n %s",
ptr->dev,
bch2_btree_id_str(i->btree), i->level, buf.buf);
printbuf_exit(&buf);
int ret3 = pwrite(ca->disk_sb.bdev->bd_fd, zeroes,
c->opts.block_size, ptr->offset << 9);
bch2_dev_put(ca);
if (ret3 != c->opts.block_size) {
bch_err(c, "pwrite error: expected %u got %i %s",
c->opts.block_size, ret, strerror(errno));
ret2 = EXIT_FAILURE;
}
}
}
i->idx--;
ret2;
}));
if (ret < 0) {
bch_err(c, "error %i walking btree nodes", ret);
break;
} else if (!ret) {
bch_err(c, "node at specified index not found");
ret = EXIT_FAILURE;
break;
}
}
bch2_trans_put(trans);
bch2_fs_stop(c);
darray_exit(&kill_nodes);
return ret < 0 ? ret : 0;
}
|