]> git.sesse.net Git - bcachefs-tools-debian/blob - cmd_debug.c
e29ceff6826978d65dfd28227208e795eca84e89
[bcachefs-tools-debian] / cmd_debug.c
1 #include <fcntl.h>
2 #include <string.h>
3 #include <sys/stat.h>
4 #include <sys/types.h>
5
6 #include "cmds.h"
7 #include "libbcachefs.h"
8 #include "qcow2.h"
9 #include "tools-util.h"
10
11 #include "libbcachefs/bcachefs.h"
12 #include "libbcachefs/bset.h"
13 #include "libbcachefs/btree_cache.h"
14 #include "libbcachefs/btree_io.h"
15 #include "libbcachefs/btree_iter.h"
16 #include "libbcachefs/buckets.h"
17 #include "libbcachefs/checksum.h"
18 #include "libbcachefs/error.h"
19 #include "libbcachefs/journal.h"
20 #include "libbcachefs/journal_io.h"
21 #include "libbcachefs/journal_seq_blacklist.h"
22 #include "libbcachefs/super.h"
23
24 static void dump_usage(void)
25 {
26         puts("bcachefs dump - dump filesystem metadata\n"
27              "Usage: bcachefs dump [OPTION]... <devices>\n"
28              "\n"
29              "Options:\n"
30              "  -o output     Output qcow2 image(s)\n"
31              "  -f            Force; overwrite when needed\n"
32              "  -j            Dump entire journal, not just dirty entries\n"
33              "  -h            Display this help and exit\n"
34              "Report bugs to <linux-bcache@vger.kernel.org>");
35 }
36
37 static void dump_one_device(struct bch_fs *c, struct bch_dev *ca, int fd,
38                             bool entire_journal)
39 {
40         struct bch_sb *sb = ca->disk_sb.sb;
41         ranges data = { 0 };
42         unsigned i;
43         int ret;
44
45         /* Superblock: */
46         range_add(&data, BCH_SB_LAYOUT_SECTOR << 9,
47                   sizeof(struct bch_sb_layout));
48
49         for (i = 0; i < sb->layout.nr_superblocks; i++)
50                 range_add(&data,
51                           le64_to_cpu(sb->layout.sb_offset[i]) << 9,
52                           vstruct_bytes(sb));
53
54         /* Journal: */
55         for (i = 0; i < ca->journal.nr; i++)
56                 if (entire_journal ||
57                     ca->journal.bucket_seq[i] >= c->journal.last_seq_ondisk) {
58                         u64 bucket = ca->journal.buckets[i];
59
60                         range_add(&data,
61                                   bucket_bytes(ca) * bucket,
62                                   bucket_bytes(ca));
63                 }
64
65         /* Btree: */
66         for (i = 0; i < BTREE_ID_NR; i++) {
67                 const struct bch_extent_ptr *ptr;
68                 struct bkey_ptrs_c ptrs;
69                 struct btree_trans trans;
70                 struct btree_iter iter;
71                 struct btree *b;
72
73                 bch2_trans_init(&trans, c, 0, 0);
74
75                 __for_each_btree_node(&trans, iter, i, POS_MIN, 0, 1, 0, b, ret) {
76                         struct btree_node_iter iter;
77                         struct bkey u;
78                         struct bkey_s_c k;
79
80                         for_each_btree_node_key_unpack(b, k, &iter, &u) {
81                                 ptrs = bch2_bkey_ptrs_c(k);
82
83                                 bkey_for_each_ptr(ptrs, ptr)
84                                         if (ptr->dev == ca->dev_idx)
85                                                 range_add(&data,
86                                                           ptr->offset << 9,
87                                                           btree_bytes(c));
88                         }
89                 }
90
91                 if (ret)
92                         die("error %s walking btree nodes", strerror(-ret));
93
94                 b = c->btree_roots[i].b;
95                 if (!btree_node_fake(b)) {
96                         ptrs = bch2_bkey_ptrs_c(bkey_i_to_s_c(&b->key));
97
98                         bkey_for_each_ptr(ptrs, ptr)
99                                 if (ptr->dev == ca->dev_idx)
100                                         range_add(&data,
101                                                   ptr->offset << 9,
102                                                   btree_bytes(c));
103                 }
104
105                 bch2_trans_iter_exit(&trans, &iter);
106                 bch2_trans_exit(&trans);
107         }
108
109         qcow2_write_image(ca->disk_sb.bdev->bd_fd, fd, &data,
110                           max_t(unsigned, btree_bytes(c) / 8, block_bytes(c)));
111         darray_exit(data);
112 }
113
114 int cmd_dump(int argc, char *argv[])
115 {
116         struct bch_opts opts = bch2_opts_empty();
117         struct bch_dev *ca;
118         char *out = NULL;
119         unsigned i, nr_devices = 0;
120         bool force = false, entire_journal = false;
121         int fd, opt;
122
123         opt_set(opts, nochanges,        true);
124         opt_set(opts, norecovery,       true);
125         opt_set(opts, degraded,         true);
126         opt_set(opts, errors,           BCH_ON_ERROR_continue);
127         opt_set(opts, fix_errors,       FSCK_OPT_NO);
128
129         while ((opt = getopt(argc, argv, "o:fjvh")) != -1)
130                 switch (opt) {
131                 case 'o':
132                         out = optarg;
133                         break;
134                 case 'f':
135                         force = true;
136                         break;
137                 case 'j':
138                         entire_journal = true;
139                         break;
140                 case 'v':
141                         opt_set(opts, verbose, true);
142                         break;
143                 case 'h':
144                         dump_usage();
145                         exit(EXIT_SUCCESS);
146                 }
147         args_shift(optind);
148
149         if (!out)
150                 die("Please supply output filename");
151
152         if (!argc)
153                 die("Please supply device(s) to check");
154
155         struct bch_fs *c = bch2_fs_open(argv, argc, opts);
156         if (IS_ERR(c))
157                 die("error opening %s: %s", argv[0], strerror(-PTR_ERR(c)));
158
159         down_read(&c->gc_lock);
160
161         for_each_online_member(ca, c, i)
162                 nr_devices++;
163
164         BUG_ON(!nr_devices);
165
166         for_each_online_member(ca, c, i) {
167                 int flags = O_WRONLY|O_CREAT|O_TRUNC;
168
169                 if (!force)
170                         flags |= O_EXCL;
171
172                 if (!c->devs[i])
173                         continue;
174
175                 char *path = nr_devices > 1
176                         ? mprintf("%s.%u", out, i)
177                         : strdup(out);
178                 fd = xopen(path, flags, 0600);
179                 free(path);
180
181                 dump_one_device(c, ca, fd, entire_journal);
182                 close(fd);
183         }
184
185         up_read(&c->gc_lock);
186
187         bch2_fs_stop(c);
188         return 0;
189 }
190
191 static void list_keys(struct bch_fs *c, enum btree_id btree_id,
192                       struct bpos start, struct bpos end)
193 {
194         struct btree_trans trans;
195         struct btree_iter iter;
196         struct bkey_s_c k;
197         struct printbuf buf = PRINTBUF;
198         int ret;
199
200         bch2_trans_init(&trans, c, 0, 0);
201
202         for_each_btree_key(&trans, iter, btree_id, start,
203                            BTREE_ITER_ALL_SNAPSHOTS|
204                            BTREE_ITER_PREFETCH, k, ret) {
205                 if (bkey_cmp(k.k->p, end) > 0)
206                         break;
207
208                 printbuf_reset(&buf);
209                 bch2_bkey_val_to_text(&buf, c, k);
210                 puts(buf.buf);
211         }
212         bch2_trans_iter_exit(&trans, &iter);
213
214         bch2_trans_exit(&trans);
215
216         printbuf_exit(&buf);
217 }
218
219 static void list_btree_formats(struct bch_fs *c, enum btree_id btree_id, unsigned level,
220                                struct bpos start, struct bpos end)
221 {
222         struct btree_trans trans;
223         struct btree_iter iter;
224         struct btree *b;
225         struct printbuf buf = PRINTBUF;
226         int ret;
227
228         bch2_trans_init(&trans, c, 0, 0);
229
230         __for_each_btree_node(&trans, iter, btree_id, start, 0, level, 0, b, ret) {
231                 if (bkey_cmp(b->key.k.p, end) > 0)
232                         break;
233
234                 printbuf_reset(&buf);
235                 bch2_btree_node_to_text(&buf, c, b);
236                 puts(buf.buf);
237         }
238         bch2_trans_iter_exit(&trans, &iter);
239
240         if (ret)
241                 die("error %s walking btree nodes", strerror(-ret));
242
243         bch2_trans_exit(&trans);
244         printbuf_exit(&buf);
245 }
246
247 static void list_nodes(struct bch_fs *c, enum btree_id btree_id, unsigned level,
248                        struct bpos start, struct bpos end)
249 {
250         struct btree_trans trans;
251         struct btree_iter iter;
252         struct btree *b;
253         struct printbuf buf = PRINTBUF;
254         int ret;
255
256         bch2_trans_init(&trans, c, 0, 0);
257
258         __for_each_btree_node(&trans, iter, btree_id, start, 0, level, 0, b, ret) {
259                 if (bkey_cmp(b->key.k.p, end) > 0)
260                         break;
261
262                 printbuf_reset(&buf);
263                 bch2_bkey_val_to_text(&buf, c, bkey_i_to_s_c(&b->key));
264                 fputs(buf.buf, stdout);
265                 putchar('\n');
266         }
267         bch2_trans_iter_exit(&trans, &iter);
268
269         if (ret)
270                 die("error %s walking btree nodes", strerror(-ret));
271
272         bch2_trans_exit(&trans);
273         printbuf_exit(&buf);
274 }
275
276 static void print_node_ondisk(struct bch_fs *c, struct btree *b)
277 {
278         struct btree_node *n_ondisk;
279         struct extent_ptr_decoded pick;
280         struct bch_dev *ca;
281         struct bio *bio;
282         unsigned offset = 0;
283
284         if (bch2_bkey_pick_read_device(c, bkey_i_to_s_c(&b->key), NULL, &pick) <= 0) {
285                 printf("error getting device to read from\n");
286                 return;
287         }
288
289         ca = bch_dev_bkey_exists(c, pick.ptr.dev);
290         if (!bch2_dev_get_ioref(ca, READ)) {
291                 printf("error getting device to read from\n");
292                 return;
293         }
294
295         n_ondisk = malloc(btree_bytes(c));
296
297         bio = bio_alloc_bioset(GFP_NOIO,
298                         buf_pages(n_ondisk, btree_bytes(c)),
299                         &c->btree_bio);
300         bio_set_dev(bio, ca->disk_sb.bdev);
301         bio->bi_opf             = REQ_OP_READ|REQ_META;
302         bio->bi_iter.bi_sector  = pick.ptr.offset;
303         bch2_bio_map(bio, n_ondisk, btree_bytes(c));
304
305         submit_bio_wait(bio);
306
307         bio_put(bio);
308         percpu_ref_put(&ca->io_ref);
309
310         while (offset < btree_sectors(c)) {
311                 struct bset *i;
312                 struct nonce nonce;
313                 struct bch_csum csum;
314                 struct bkey_packed *k;
315                 unsigned sectors;
316
317                 if (!offset) {
318                         i = &n_ondisk->keys;
319
320                         if (!bch2_checksum_type_valid(c, BSET_CSUM_TYPE(i)))
321                                 die("unknown checksum type");
322
323                         nonce = btree_nonce(i, offset << 9);
324                         csum = csum_vstruct(c, BSET_CSUM_TYPE(i), nonce, n_ondisk);
325
326                         if (bch2_crc_cmp(csum, n_ondisk->csum))
327                                 die("invalid checksum\n");
328
329                         bset_encrypt(c, i, offset << 9);
330
331                         sectors = vstruct_sectors(n_ondisk, c->block_bits);
332                 } else {
333                         struct btree_node_entry *bne = (void *) n_ondisk + (offset << 9);
334
335                         i = &bne->keys;
336
337                         if (i->seq != n_ondisk->keys.seq)
338                                 break;
339
340                         if (!bch2_checksum_type_valid(c, BSET_CSUM_TYPE(i)))
341                                 die("unknown checksum type");
342
343                         nonce = btree_nonce(i, offset << 9);
344                         csum = csum_vstruct(c, BSET_CSUM_TYPE(i), nonce, bne);
345
346                         if (bch2_crc_cmp(csum, bne->csum))
347                                 die("invalid checksum");
348
349                         bset_encrypt(c, i, offset << 9);
350
351                         sectors = vstruct_sectors(bne, c->block_bits);
352                 }
353
354                 fprintf(stdout, "  offset %u version %u, journal seq %llu\n",
355                         offset,
356                         le16_to_cpu(i->version),
357                         le64_to_cpu(i->journal_seq));
358                 offset += sectors;
359
360                 for (k = i->start; k != vstruct_last(i); k = bkey_next(k)) {
361                         struct bkey u;
362                         struct printbuf buf = PRINTBUF;
363
364                         bch2_bkey_val_to_text(&buf, c, bkey_disassemble(b, k, &u));
365                         fprintf(stdout, "    %s\n", buf.buf);
366
367                         printbuf_exit(&buf);
368                 }
369         }
370
371         free(n_ondisk);
372 }
373
374 static void list_nodes_ondisk(struct bch_fs *c, enum btree_id btree_id, unsigned level,
375                               struct bpos start, struct bpos end)
376 {
377         struct btree_trans trans;
378         struct btree_iter iter;
379         struct btree *b;
380         struct printbuf buf = PRINTBUF;
381         int ret;
382
383         bch2_trans_init(&trans, c, 0, 0);
384
385         __for_each_btree_node(&trans, iter, btree_id, start, 0, level, 0, b, ret) {
386                 if (bkey_cmp(b->key.k.p, end) > 0)
387                         break;
388
389                 printbuf_reset(&buf);
390                 bch2_bkey_val_to_text(&buf, c, bkey_i_to_s_c(&b->key));
391                 fputs(buf.buf, stdout);
392                 putchar('\n');
393
394                 print_node_ondisk(c, b);
395         }
396         bch2_trans_iter_exit(&trans, &iter);
397
398         if (ret)
399                 die("error %s walking btree nodes", strerror(-ret));
400
401         bch2_trans_exit(&trans);
402         printbuf_exit(&buf);
403 }
404
405 static void list_nodes_keys(struct bch_fs *c, enum btree_id btree_id, unsigned level,
406                             struct bpos start, struct bpos end)
407 {
408         struct btree_trans trans;
409         struct btree_iter iter;
410         struct btree_node_iter node_iter;
411         struct bkey unpacked;
412         struct bkey_s_c k;
413         struct btree *b;
414         struct printbuf buf = PRINTBUF;
415         int ret;
416
417         bch2_trans_init(&trans, c, 0, 0);
418
419         __for_each_btree_node(&trans, iter, btree_id, start, 0, level, 0, b, ret) {
420                 if (bkey_cmp(b->key.k.p, end) > 0)
421                         break;
422
423                 printbuf_reset(&buf);
424                 bch2_btree_node_to_text(&buf, c, b);
425                 fputs(buf.buf, stdout);
426
427                 for_each_btree_node_key_unpack(b, k, &node_iter, &unpacked) {
428                         printbuf_reset(&buf);
429                         bch2_bkey_val_to_text(&buf, c, k);
430                         putchar('\t');
431                         puts(buf.buf);
432                 }
433         }
434         bch2_trans_iter_exit(&trans, &iter);
435
436         if (ret)
437                 die("error %s walking btree nodes", strerror(-ret));
438
439         bch2_trans_exit(&trans);
440         printbuf_exit(&buf);
441 }
442
443 static void list_keys_usage(void)
444 {
445         puts("bcachefs list - list filesystem metadata to stdout\n"
446              "Usage: bcachefs list [OPTION]... <devices>\n"
447              "\n"
448              "Options:\n"
449              "  -b (extents|inodes|dirents|xattrs)    Btree to list from\n"
450              "  -l level                              Btree depth to descend to (0 == leaves)\n"
451              "  -s inode:offset                       Start position to list from\n"
452              "  -e inode:offset                       End position\n"
453              "  -i inode                              List keys for a given inode number\n"
454              "  -m (keys|formats|nodes|nodes_ondisk|nodes_keys)\n"
455              "                                        List mode\n"
456              "  -f                                    Check (fsck) the filesystem first\n"
457              "  -v                                    Verbose mode\n"
458              "  -h                                    Display this help and exit\n"
459              "Report bugs to <linux-bcache@vger.kernel.org>");
460 }
461
462 #define LIST_MODES()            \
463         x(keys)                 \
464         x(formats)              \
465         x(nodes)                \
466         x(nodes_ondisk)         \
467         x(nodes_keys)
468
469 enum list_modes {
470 #define x(n)    LIST_MODE_##n,
471         LIST_MODES()
472 #undef x
473 };
474
475 static const char * const list_modes[] = {
476 #define x(n)    #n,
477         LIST_MODES()
478 #undef x
479         NULL
480 };
481
482 int cmd_list(int argc, char *argv[])
483 {
484         struct bch_opts opts = bch2_opts_empty();
485         enum btree_id btree_id_start    = 0;
486         enum btree_id btree_id_end      = BTREE_ID_NR;
487         enum btree_id btree_id;
488         unsigned level = 0;
489         struct bpos start = POS_MIN, end = POS_MAX;
490         u64 inum = 0;
491         int mode = 0, opt;
492
493         opt_set(opts, nochanges,        true);
494         opt_set(opts, norecovery,       true);
495         opt_set(opts, degraded,         true);
496         opt_set(opts, errors,           BCH_ON_ERROR_continue);
497
498         while ((opt = getopt(argc, argv, "b:l:s:e:i:m:fvh")) != -1)
499                 switch (opt) {
500                 case 'b':
501                         btree_id_start = read_string_list_or_die(optarg,
502                                                 bch2_btree_ids, "btree id");
503                         btree_id_end = btree_id_start + 1;
504                         break;
505                 case 'l':
506                         if (kstrtouint(optarg, 10, &level) || level >= BTREE_MAX_DEPTH)
507                                 die("invalid level");
508                         break;
509                 case 's':
510                         start   = bpos_parse(optarg);
511                         break;
512                 case 'e':
513                         end     = bpos_parse(optarg);
514                         break;
515                 case 'i':
516                         if (kstrtoull(optarg, 10, &inum))
517                                 die("invalid inode %s", optarg);
518                         start   = POS(inum, 0);
519                         end     = POS(inum + 1, 0);
520                         break;
521                 case 'm':
522                         mode = read_string_list_or_die(optarg,
523                                                 list_modes, "list mode");
524                         break;
525                 case 'f':
526                         opt_set(opts, fix_errors, FSCK_OPT_YES);
527                         opt_set(opts, norecovery, false);
528                         break;
529                 case 'v':
530                         opt_set(opts, verbose, true);
531                         break;
532                 case 'h':
533                         list_keys_usage();
534                         exit(EXIT_SUCCESS);
535                 }
536         args_shift(optind);
537
538         if (!argc)
539                 die("Please supply device(s)");
540
541         struct bch_fs *c = bch2_fs_open(argv, argc, opts);
542         if (IS_ERR(c))
543                 die("error opening %s: %s", argv[0], strerror(-PTR_ERR(c)));
544
545
546         for (btree_id = btree_id_start;
547              btree_id < btree_id_end;
548              btree_id++) {
549                 switch (mode) {
550                 case LIST_MODE_keys:
551                         list_keys(c, btree_id, start, end);
552                         break;
553                 case LIST_MODE_formats:
554                         list_btree_formats(c, btree_id, level, start, end);
555                         break;
556                 case LIST_MODE_nodes:
557                         list_nodes(c, btree_id, level, start, end);
558                         break;
559                 case LIST_MODE_nodes_ondisk:
560                         list_nodes_ondisk(c, btree_id, level, start, end);
561                         break;
562                 case LIST_MODE_nodes_keys:
563                         list_nodes_keys(c, btree_id, level, start, end);
564                         break;
565                 default:
566                         die("Invalid mode");
567                 }
568         }
569
570         bch2_fs_stop(c);
571         return 0;
572 }
573
574 static void list_journal_usage(void)
575 {
576         puts("bcachefs list_journal - print contents of journal\n"
577              "Usage: bcachefs list_journal [OPTION]... <devices>\n"
578              "\n"
579              "Options:\n"
580              "  -a            Read entire journal, not just dirty entries\n"
581              "  -n            Number of journal entries to print, starting from the most recent\n"
582              "  -h            Display this help and exit\n"
583              "Report bugs to <linux-bcache@vger.kernel.org>");
584 }
585
586 static void star_start_of_lines(char *buf)
587 {
588         char *p = buf;
589
590         if (*p == ' ')
591                 *p = '*';
592
593         while ((p = strstr(p, "\n ")))
594                 p[1] = '*';
595 }
596
597 int cmd_list_journal(int argc, char *argv[])
598 {
599         struct bch_opts opts = bch2_opts_empty();
600         u32 nr_entries = U32_MAX;
601         int opt;
602
603         opt_set(opts, nochanges,        true);
604         opt_set(opts, norecovery,       true);
605         opt_set(opts, degraded,         true);
606         opt_set(opts, errors,           BCH_ON_ERROR_continue);
607         opt_set(opts, fix_errors,       FSCK_OPT_YES);
608         opt_set(opts, keep_journal,     true);
609         opt_set(opts, read_journal_only,true);
610
611         while ((opt = getopt(argc, argv, "an:h")) != -1)
612                 switch (opt) {
613                 case 'a':
614                         opt_set(opts, read_entire_journal, true);
615                         break;
616                 case 'n':
617                         nr_entries = kstrtouint(optarg, 10, &nr_entries);
618                         opt_set(opts, read_entire_journal, true);
619                         break;
620                 case 'h':
621                         list_journal_usage();
622                         exit(EXIT_SUCCESS);
623                 }
624         args_shift(optind);
625
626         if (!argc)
627                 die("Please supply device(s) to open");
628
629         struct bch_fs *c = bch2_fs_open(argv, argc, opts);
630         if (IS_ERR(c))
631                 die("error opening %s: %s", argv[0], strerror(-PTR_ERR(c)));
632
633         struct journal_replay *p, **_p;
634         struct genradix_iter iter;
635         struct jset_entry *entry;
636         struct printbuf buf = PRINTBUF;
637
638         genradix_for_each(&c->journal_entries, iter, _p) {
639                 p = *_p;
640                 if (!p)
641                         continue;
642
643                 if (le64_to_cpu(p->j.seq) + nr_entries < atomic64_read(&c->journal.seq))
644                         continue;
645
646                 bool blacklisted =
647                         bch2_journal_seq_is_blacklisted(c,
648                                         le64_to_cpu(p->j.seq), false);
649
650                 if (blacklisted)
651                         printf("blacklisted ");
652
653                 printf("journal entry       %llu\n", le64_to_cpu(p->j.seq));
654
655                 printbuf_reset(&buf);
656
657                 pr_buf(&buf,
658                        "  version         %u\n"
659                        "  last seq        %llu\n"
660                        "  flush           %u\n"
661                        "  written at      ",
662                        le32_to_cpu(p->j.version),
663                        le64_to_cpu(p->j.last_seq),
664                        !JSET_NO_FLUSH(&p->j));
665                 bch2_journal_ptrs_to_text(&buf, c, p);
666
667                 if (blacklisted)
668                         star_start_of_lines(buf.buf);
669                 printf("%s\n", buf.buf);
670
671                 vstruct_for_each(&p->j, entry) {
672                         printbuf_reset(&buf);
673
674                         /*
675                          * log entries denote the start of a new transaction
676                          * commit:
677                          */
678                         if (entry->type == BCH_JSET_ENTRY_log && !entry->level)
679                                 pr_newline(&buf);
680                         pr_indent_push(&buf, 4);
681                         bch2_journal_entry_to_text(&buf, c, entry);
682
683                         if (blacklisted)
684                                 star_start_of_lines(buf.buf);
685                         printf("%s\n", buf.buf);
686                 }
687         }
688
689         printbuf_exit(&buf);
690         bch2_fs_stop(c);
691         return 0;
692 }
693
694 static void kill_btree_node_usage(void)
695 {
696         puts("bcachefs kill_btree_node - make btree nodes unreadable\n"
697              "Usage: bcachefs kill_btree_node [OPTION]... <devices>\n"
698              "\n"
699              "Options:\n"
700              "  -b (extents|inodes|dirents|xattrs)    Btree to delete from\n"
701              "  -l level                              Levle to delete from (0 == leaves)\n"
702              "  -i index                              Index of btree node to kill\n"
703              "  -h                                    Display this help and exit\n"
704              "Report bugs to <linux-bcache@vger.kernel.org>");
705 }
706
707 int cmd_kill_btree_node(int argc, char *argv[])
708 {
709         struct bch_opts opts = bch2_opts_empty();
710         enum btree_id btree_id = 0;
711         unsigned level = 0;
712         u64 node_index = 0;
713         int opt;
714
715         opt_set(opts, read_only,        true);
716
717         while ((opt = getopt(argc, argv, "b:l:i:h")) != -1)
718                 switch (opt) {
719                 case 'b':
720                         btree_id = read_string_list_or_die(optarg,
721                                                 bch2_btree_ids, "btree id");
722                         break;
723                 case 'l':
724                         if (kstrtouint(optarg, 10, &level) || level >= BTREE_MAX_DEPTH)
725                                 die("invalid level");
726                         break;
727                 case 'i':
728                         if (kstrtoull(optarg, 10, &node_index))
729                                 die("invalid index %s", optarg);
730                         break;
731                 case 'h':
732                         kill_btree_node_usage();
733                         exit(EXIT_SUCCESS);
734                 }
735         args_shift(optind);
736
737         if (!argc)
738                 die("Please supply device(s)");
739
740         struct bch_fs *c = bch2_fs_open(argv, argc, opts);
741         if (IS_ERR(c))
742                 die("error opening %s: %s", argv[0], strerror(-PTR_ERR(c)));
743
744         struct btree_trans trans;
745         struct btree_iter iter;
746         struct btree *b;
747         int ret;
748         void *zeroes;
749
750         ret = posix_memalign(&zeroes, c->opts.block_size, c->opts.block_size);
751         if (ret)
752                 die("error %s from posix_memalign", strerror(ret));
753
754         bch2_trans_init(&trans, c, 0, 0);
755
756         __for_each_btree_node(&trans, iter, btree_id, POS_MIN, 0, level, 0, b, ret) {
757                 if (b->c.level != level)
758                         continue;
759
760                 if (!node_index) {
761                         struct bkey_ptrs_c ptrs = bch2_bkey_ptrs_c(bkey_i_to_s_c(&b->key));
762                         const struct bch_extent_ptr *ptr;
763
764                         struct printbuf buf = PRINTBUF;
765
766                         bch2_bkey_val_to_text(&buf, c, bkey_i_to_s_c(&b->key));
767                         bch_info(c, "killing btree node %s", buf.buf);
768                         printbuf_exit(&buf);
769
770                         bkey_for_each_ptr(ptrs, ptr) {
771                                 struct bch_dev *ca = bch_dev_bkey_exists(c, ptr->dev);
772
773                                 ret = pwrite(ca->disk_sb.bdev->bd_fd, zeroes,
774                                              c->opts.block_size, ptr->offset << 9);
775                                 if (ret != c->opts.block_size) {
776                                         bch_err(c, "pwrite error: expected %u got %i %s",
777                                                 c->opts.block_size, ret, strerror(errno));
778                                         ret = EXIT_FAILURE;
779                                         goto done;
780                                 }
781                         }
782                         goto done;
783                 }
784
785                 node_index--;
786         }
787         if (ret)
788                 bch_err(c, "error %i walking btree nodes", ret);
789         else
790                 bch_err(c, "node at specified index not found");
791         ret = EXIT_FAILURE;
792 done:
793         bch2_trans_iter_exit(&trans, &iter);
794         bch2_trans_exit(&trans);
795
796         bch2_fs_stop(c);
797         return ret;
798 }