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