]> git.sesse.net Git - bcachefs-tools-debian/blob - cmd_debug.c
Improved functionality for cmd_list
[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_iter.h"
15 #include "libbcachefs/buckets.h"
16 #include "libbcachefs/error.h"
17 #include "libbcachefs/journal.h"
18 #include "libbcachefs/journal_io.h"
19 #include "libbcachefs/super.h"
20
21 static void dump_usage(void)
22 {
23         puts("bcachefs dump - dump filesystem metadata\n"
24              "Usage: bcachefs dump [OPTION]... <devices>\n"
25              "\n"
26              "Options:\n"
27              "  -o output     Output qcow2 image(s)\n"
28              "  -f            Force; overwrite when needed\n"
29              "  -h            Display this help and exit\n"
30              "Report bugs to <linux-bcache@vger.kernel.org>");
31 }
32
33 static void dump_one_device(struct bch_fs *c, struct bch_dev *ca, int fd)
34 {
35         struct bch_sb *sb = ca->disk_sb.sb;
36         ranges data;
37         unsigned i;
38
39         darray_init(data);
40
41         /* Superblock: */
42         range_add(&data, BCH_SB_LAYOUT_SECTOR << 9,
43                   sizeof(struct bch_sb_layout));
44
45         for (i = 0; i < sb->layout.nr_superblocks; i++)
46                 range_add(&data,
47                           le64_to_cpu(sb->layout.sb_offset[i]) << 9,
48                           vstruct_bytes(sb));
49
50         /* Journal: */
51         for (i = 0; i < ca->journal.nr; i++)
52                 if (ca->journal.bucket_seq[i] >= c->journal.last_seq_ondisk) {
53                         u64 bucket = ca->journal.buckets[i];
54
55                         range_add(&data,
56                                   bucket_bytes(ca) * bucket,
57                                   bucket_bytes(ca));
58                 }
59
60         /* Btree: */
61         for (i = 0; i < BTREE_ID_NR; i++) {
62                 const struct bch_extent_ptr *ptr;
63                 struct bkey_ptrs_c ptrs;
64                 struct btree_trans trans;
65                 struct btree_iter *iter;
66                 struct btree *b;
67
68                 bch2_trans_init(&trans, c, 0, 0);
69
70                 __for_each_btree_node(&trans, iter, i, POS_MIN, 0, 1, 0, b) {
71                         struct btree_node_iter iter;
72                         struct bkey u;
73                         struct bkey_s_c k;
74
75                         for_each_btree_node_key_unpack(b, k, &iter, &u) {
76                                 ptrs = bch2_bkey_ptrs_c(k);
77
78                                 bkey_for_each_ptr(ptrs, ptr)
79                                         if (ptr->dev == ca->dev_idx)
80                                                 range_add(&data,
81                                                           ptr->offset << 9,
82                                                           btree_bytes(c));
83                         }
84                 }
85
86                 b = c->btree_roots[i].b;
87                 if (!btree_node_fake(b)) {
88                         ptrs = bch2_bkey_ptrs_c(bkey_i_to_s_c(&b->key));
89
90                         bkey_for_each_ptr(ptrs, ptr)
91                                 if (ptr->dev == ca->dev_idx)
92                                         range_add(&data,
93                                                   ptr->offset << 9,
94                                                   btree_bytes(c));
95                 }
96                 bch2_trans_exit(&trans);
97         }
98
99         qcow2_write_image(ca->disk_sb.bdev->bd_fd, fd, &data,
100                           max_t(unsigned, btree_bytes(c) / 8, block_bytes(c)));
101 }
102
103 int cmd_dump(int argc, char *argv[])
104 {
105         struct bch_opts opts = bch2_opts_empty();
106         struct bch_dev *ca;
107         char *out = NULL;
108         unsigned i, nr_devices = 0;
109         bool force = false;
110         int fd, opt;
111
112         opt_set(opts, nochanges,        true);
113         opt_set(opts, norecovery,       true);
114         opt_set(opts, degraded,         true);
115         opt_set(opts, errors,           BCH_ON_ERROR_CONTINUE);
116         opt_set(opts, fix_errors,       FSCK_OPT_YES);
117
118         while ((opt = getopt(argc, argv, "o:fvh")) != -1)
119                 switch (opt) {
120                 case 'o':
121                         out = optarg;
122                         break;
123                 case 'f':
124                         force = true;
125                         break;
126                 case 'v':
127                         opt_set(opts, verbose, true);
128                         break;
129                 case 'h':
130                         dump_usage();
131                         exit(EXIT_SUCCESS);
132                 }
133         args_shift(optind);
134
135         if (!out)
136                 die("Please supply output filename");
137
138         if (!argc)
139                 die("Please supply device(s) to check");
140
141         struct bch_fs *c = bch2_fs_open(argv, argc, opts);
142         if (IS_ERR(c))
143                 die("error opening %s: %s", argv[0], strerror(-PTR_ERR(c)));
144
145         down_read(&c->gc_lock);
146
147         for_each_online_member(ca, c, i)
148                 nr_devices++;
149
150         BUG_ON(!nr_devices);
151
152         for_each_online_member(ca, c, i) {
153                 int flags = O_WRONLY|O_CREAT|O_TRUNC;
154
155                 if (!force)
156                         flags |= O_EXCL;
157
158                 if (!c->devs[i])
159                         continue;
160
161                 char *path = nr_devices > 1
162                         ? mprintf("%s.%u", out, i)
163                         : strdup(out);
164                 fd = xopen(path, flags, 0600);
165                 free(path);
166
167                 dump_one_device(c, ca, fd);
168                 close(fd);
169         }
170
171         up_read(&c->gc_lock);
172
173         bch2_fs_stop(c);
174         return 0;
175 }
176
177 static void list_keys(struct bch_fs *c, enum btree_id btree_id,
178                       struct bpos start, struct bpos end)
179 {
180         struct btree_trans trans;
181         struct btree_iter *iter;
182         struct bkey_s_c k;
183         char buf[512];
184         int ret;
185
186         bch2_trans_init(&trans, c, 0, 0);
187
188         for_each_btree_key(&trans, iter, btree_id, start,
189                            BTREE_ITER_PREFETCH, k, ret) {
190                 if (bkey_cmp(k.k->p, end) > 0)
191                         break;
192
193                 bch2_bkey_val_to_text(&PBUF(buf), c, k);
194                 puts(buf);
195         }
196         bch2_trans_exit(&trans);
197 }
198
199 static void list_btree_formats(struct bch_fs *c, enum btree_id btree_id,
200                                struct bpos start, struct bpos end)
201 {
202         struct btree_trans trans;
203         struct btree_iter *iter;
204         struct btree *b;
205         char buf[4096];
206
207         bch2_trans_init(&trans, c, 0, 0);
208
209         for_each_btree_node(&trans, iter, btree_id, start, 0, b) {
210                 if (bkey_cmp(b->key.k.p, end) > 0)
211                         break;
212
213                 bch2_btree_node_to_text(&PBUF(buf), c, b);
214                 puts(buf);
215         }
216         bch2_trans_exit(&trans);
217 }
218
219 static void list_nodes(struct bch_fs *c, enum btree_id btree_id,
220                             struct bpos start, struct bpos end)
221 {
222         struct btree_trans trans;
223         struct btree_iter *iter;
224         struct btree *b;
225         char buf[4096];
226
227         bch2_trans_init(&trans, c, 0, 0);
228
229         for_each_btree_node(&trans, iter, btree_id, start, 0, b) {
230                 if (bkey_cmp(b->key.k.p, end) > 0)
231                         break;
232
233                 bch2_bkey_val_to_text(&PBUF(buf), c, bkey_i_to_s_c(&b->key));
234                 fputs(buf, stdout);
235                 putchar('\n');
236         }
237         bch2_trans_exit(&trans);
238 }
239
240 static void list_nodes_keys(struct bch_fs *c, enum btree_id btree_id,
241                             struct bpos start, struct bpos end)
242 {
243         struct btree_trans trans;
244         struct btree_iter *iter;
245         struct btree_node_iter node_iter;
246         struct bkey unpacked;
247         struct bkey_s_c k;
248         struct btree *b;
249         char buf[4096];
250
251         bch2_trans_init(&trans, c, 0, 0);
252
253         for_each_btree_node(&trans, iter, btree_id, start, 0, b) {
254                 if (bkey_cmp(b->key.k.p, end) > 0)
255                         break;
256
257                 bch2_btree_node_to_text(&PBUF(buf), c, b);
258                 fputs(buf, stdout);
259
260                 for_each_btree_node_key_unpack(b, k, &node_iter, &unpacked) {
261                         bch2_bkey_val_to_text(&PBUF(buf), c, k);
262                         putchar('\t');
263                         puts(buf);
264                 }
265         }
266         bch2_trans_exit(&trans);
267 }
268
269 static struct bpos parse_pos(char *buf)
270 {
271         char *s = buf, *field;
272         u64 inode_v = 0, offset_v = 0;
273
274         if (!(field = strsep(&s, ":")) ||
275             kstrtoull(field, 10, &inode_v))
276                 die("invalid bpos %s", buf);
277
278         if ((field = strsep(&s, ":")) &&
279             kstrtoull(field, 10, &offset_v))
280                 die("invalid bpos %s", buf);
281
282         if (s)
283                 die("invalid bpos %s", buf);
284
285         return (struct bpos) { .inode = inode_v, .offset = offset_v };
286 }
287
288 static void list_keys_usage(void)
289 {
290         puts("bcachefs list - list filesystem metadata to stdout\n"
291              "Usage: bcachefs list [OPTION]... <devices>\n"
292              "\n"
293              "Options:\n"
294              "  -b (extents|inodes|dirents|xattrs)    Btree to list from\n"
295              "  -s inode:offset                       Start position to list from\n"
296              "  -e inode:offset                       End position\n"
297              "  -i inode                              List keys for a given inode number\n"
298              "  -m (keys|formats)                     List mode\n"
299              "  -f                                    Check (fsck) the filesystem first\n"
300              "  -v                                    Verbose mode\n"
301              "  -h                                    Display this help and exit\n"
302              "Report bugs to <linux-bcache@vger.kernel.org>");
303 }
304
305 static const char * const list_modes[] = {
306         "keys",
307         "formats",
308         "nodes",
309         "nodes_keys",
310         NULL
311 };
312
313 int cmd_list(int argc, char *argv[])
314 {
315         struct bch_opts opts = bch2_opts_empty();
316         enum btree_id btree_id_start    = 0;
317         enum btree_id btree_id_end      = BTREE_ID_NR;
318         enum btree_id btree_id;
319         struct bpos start = POS_MIN, end = POS_MAX;
320         u64 inum;
321         int mode = 0, opt;
322
323         opt_set(opts, nochanges,        true);
324         opt_set(opts, norecovery,       true);
325         opt_set(opts, degraded,         true);
326         opt_set(opts, errors,           BCH_ON_ERROR_CONTINUE);
327
328         while ((opt = getopt(argc, argv, "b:s:e:i:m:fvh")) != -1)
329                 switch (opt) {
330                 case 'b':
331                         btree_id_start = read_string_list_or_die(optarg,
332                                                 bch2_btree_ids, "btree id");
333                         btree_id_end = btree_id_start + 1;
334                         break;
335                 case 's':
336                         start   = parse_pos(optarg);
337                         break;
338                 case 'e':
339                         end     = parse_pos(optarg);
340                         break;
341                 case 'i':
342                         if (kstrtoull(optarg, 10, &inum))
343                                 die("invalid inode %s", optarg);
344                         start   = POS(inum, 0);
345                         end     = POS(inum + 1, 0);
346                         break;
347                 case 'm':
348                         mode = read_string_list_or_die(optarg,
349                                                 list_modes, "list mode");
350                         break;
351                 case 'f':
352                         opt_set(opts, fix_errors, FSCK_OPT_YES);
353                         opt_set(opts, norecovery, false);
354                         break;
355                 case 'v':
356                         opt_set(opts, verbose, true);
357                         break;
358                 case 'h':
359                         list_keys_usage();
360                         exit(EXIT_SUCCESS);
361                 }
362         args_shift(optind);
363
364         if (!argc)
365                 die("Please supply device(s)");
366
367         struct bch_fs *c = bch2_fs_open(argv, argc, opts);
368         if (IS_ERR(c))
369                 die("error opening %s: %s", argv[0], strerror(-PTR_ERR(c)));
370
371
372         for (btree_id = btree_id_start;
373              btree_id < btree_id_end;
374              btree_id++) {
375                 switch (mode) {
376                 case 0:
377                         list_keys(c, btree_id, start, end);
378                         break;
379                 case 1:
380                         list_btree_formats(c, btree_id, start, end);
381                         break;
382                 case 2:
383                         list_nodes(c, btree_id, start, end);
384                         break;
385                 case 3:
386                         list_nodes_keys(c, btree_id, start, end);
387                         break;
388                 default:
389                         die("Invalid mode");
390                 }
391         }
392
393         bch2_fs_stop(c);
394         return 0;
395 }
396
397 static void list_journal_usage(void)
398 {
399         puts("bcachefs list_journal - print contents of journal\n"
400              "Usage: bcachefs list_journal [OPTION]... <devices>\n"
401              "\n"
402              "Options:\n"
403              "  -h            Display this help and exit\n"
404              "Report bugs to <linux-bcache@vger.kernel.org>");
405 }
406
407 int cmd_list_journal(int argc, char *argv[])
408 {
409         struct bch_opts opts = bch2_opts_empty();
410         int opt;
411
412         opt_set(opts, nochanges,        true);
413         opt_set(opts, norecovery,       true);
414         opt_set(opts, degraded,         true);
415         opt_set(opts, errors,           BCH_ON_ERROR_CONTINUE);
416         opt_set(opts, fix_errors,       FSCK_OPT_YES);
417         opt_set(opts, keep_journal,     true);
418
419         while ((opt = getopt(argc, argv, "h")) != -1)
420                 switch (opt) {
421                 case 'h':
422                         list_journal_usage();
423                         exit(EXIT_SUCCESS);
424                 }
425         args_shift(optind);
426
427         if (!argc)
428                 die("Please supply device(s) to open");
429
430         struct bch_fs *c = bch2_fs_open(argv, argc, opts);
431         if (IS_ERR(c))
432                 die("error opening %s: %s", argv[0], strerror(-PTR_ERR(c)));
433
434         struct journal_replay *p;
435         struct jset_entry *entry;
436         struct bkey_i *k, *_n;
437
438         /* This could be greatly expanded: */
439
440         list_for_each_entry(p, &c->journal_entries, list) {
441                 printf("journal entry   %8llu\n"
442                        "    version     %8u\n"
443                        "    last seq    %8llu\n"
444                        "    read clock  %8u\n"
445                        "    write clock %8u\n"
446                        ,
447                        le64_to_cpu(p->j.seq),
448                        le32_to_cpu(p->j.seq),
449                        le64_to_cpu(p->j.last_seq),
450                        le16_to_cpu(p->j.read_clock),
451                        le16_to_cpu(p->j.write_clock));
452
453                 for_each_jset_key(k, _n, entry, &p->j) {
454                         char buf[200];
455
456                         bch2_bkey_val_to_text(&PBUF(buf), c, bkey_i_to_s_c(k));
457                         printf("btree %s l %u: %s\n",
458                                bch2_btree_ids[entry->btree_id],
459                                entry->level,
460                                buf);
461                 }
462         }
463
464         bch2_fs_stop(c);
465         return 0;
466 }