]> git.sesse.net Git - bcachefs-tools-debian/blob - cmd_debug.c
Update log2.h from linux kernel
[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_keys(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_node_iter node_iter;
225         struct bkey unpacked;
226         struct bkey_s_c k;
227         struct btree *b;
228         char buf[4096];
229
230         bch2_trans_init(&trans, c, 0, 0);
231
232         for_each_btree_node(&trans, iter, btree_id, start, 0, b) {
233                 if (bkey_cmp(b->key.k.p, end) > 0)
234                         break;
235
236                 bch2_btree_node_to_text(&PBUF(buf), c, b);
237                 fputs(buf, stdout);
238
239                 for_each_btree_node_key_unpack(b, k, &node_iter, &unpacked) {
240                         bch2_bkey_val_to_text(&PBUF(buf), c, k);
241                         putchar('\t');
242                         puts(buf);
243                 }
244         }
245         bch2_trans_exit(&trans);
246 }
247
248 static struct bpos parse_pos(char *buf)
249 {
250         char *s = buf, *field;
251         u64 inode_v = 0, offset_v = 0;
252
253         if (!(field = strsep(&s, ":")) ||
254             kstrtoull(field, 10, &inode_v))
255                 die("invalid bpos %s", buf);
256
257         if ((field = strsep(&s, ":")) &&
258             kstrtoull(field, 10, &offset_v))
259                 die("invalid bpos %s", buf);
260
261         if (s)
262                 die("invalid bpos %s", buf);
263
264         return (struct bpos) { .inode = inode_v, .offset = offset_v };
265 }
266
267 static void list_keys_usage(void)
268 {
269         puts("bcachefs list - list filesystem metadata to stdout\n"
270              "Usage: bcachefs list [OPTION]... <devices>\n"
271              "\n"
272              "Options:\n"
273              "  -b (extents|inodes|dirents|xattrs)    Btree to list from\n"
274              "  -s inode:offset                       Start position to list from\n"
275              "  -e inode:offset                       End position\n"
276              "  -i inode                              List keys for a given inode number\n"
277              "  -m (keys|formats)                     List mode\n"
278              "  -f                                    Check (fsck) the filesystem first\n"
279              "  -v                                    Verbose mode\n"
280              "  -h                                    Display this help and exit\n"
281              "Report bugs to <linux-bcache@vger.kernel.org>");
282 }
283
284 static const char * const list_modes[] = {
285         "keys",
286         "formats",
287         "nodes",
288         NULL
289 };
290
291 int cmd_list(int argc, char *argv[])
292 {
293         struct bch_opts opts = bch2_opts_empty();
294         enum btree_id btree_id = BTREE_ID_EXTENTS;
295         struct bpos start = POS_MIN, end = POS_MAX;
296         u64 inum;
297         int mode = 0, opt;
298
299         opt_set(opts, nochanges,        true);
300         opt_set(opts, norecovery,       true);
301         opt_set(opts, degraded,         true);
302         opt_set(opts, errors,           BCH_ON_ERROR_CONTINUE);
303
304         while ((opt = getopt(argc, argv, "b:s:e:i:m:fvh")) != -1)
305                 switch (opt) {
306                 case 'b':
307                         btree_id = read_string_list_or_die(optarg,
308                                                 bch2_btree_ids, "btree id");
309                         break;
310                 case 's':
311                         start   = parse_pos(optarg);
312                         break;
313                 case 'e':
314                         end     = parse_pos(optarg);
315                         break;
316                 case 'i':
317                         if (kstrtoull(optarg, 10, &inum))
318                                 die("invalid inode %s", optarg);
319                         start   = POS(inum, 0);
320                         end     = POS(inum + 1, 0);
321                         break;
322                 case 'm':
323                         mode = read_string_list_or_die(optarg,
324                                                 list_modes, "list mode");
325                         break;
326                 case 'f':
327                         opt_set(opts, fix_errors, FSCK_OPT_YES);
328                         opt_set(opts, norecovery, false);
329                         break;
330                 case 'v':
331                         opt_set(opts, verbose, true);
332                         break;
333                 case 'h':
334                         list_keys_usage();
335                         exit(EXIT_SUCCESS);
336                 }
337         args_shift(optind);
338
339         if (!argc)
340                 die("Please supply device(s)");
341
342         struct bch_fs *c = bch2_fs_open(argv, argc, opts);
343         if (IS_ERR(c))
344                 die("error opening %s: %s", argv[0], strerror(-PTR_ERR(c)));
345
346         switch (mode) {
347         case 0:
348                 list_keys(c, btree_id, start, end);
349                 break;
350         case 1:
351                 list_btree_formats(c, btree_id, start, end);
352                 break;
353         case 2:
354                 list_nodes_keys(c, btree_id, start, end);
355                 break;
356         default:
357                 die("Invalid mode");
358         }
359
360         bch2_fs_stop(c);
361         return 0;
362 }
363
364 static void list_journal_usage(void)
365 {
366         puts("bcachefs list_journal - print contents of journal\n"
367              "Usage: bcachefs list_journal [OPTION]... <devices>\n"
368              "\n"
369              "Options:\n"
370              "  -h            Display this help and exit\n"
371              "Report bugs to <linux-bcache@vger.kernel.org>");
372 }
373
374 int cmd_list_journal(int argc, char *argv[])
375 {
376         struct bch_opts opts = bch2_opts_empty();
377         int opt;
378
379         opt_set(opts, nochanges,        true);
380         opt_set(opts, norecovery,       true);
381         opt_set(opts, degraded,         true);
382         opt_set(opts, errors,           BCH_ON_ERROR_CONTINUE);
383         opt_set(opts, fix_errors,       FSCK_OPT_YES);
384         opt_set(opts, keep_journal,     true);
385
386         while ((opt = getopt(argc, argv, "h")) != -1)
387                 switch (opt) {
388                 case 'h':
389                         list_journal_usage();
390                         exit(EXIT_SUCCESS);
391                 }
392         args_shift(optind);
393
394         if (!argc)
395                 die("Please supply device(s) to open");
396
397         struct bch_fs *c = bch2_fs_open(argv, argc, opts);
398         if (IS_ERR(c))
399                 die("error opening %s: %s", argv[0], strerror(-PTR_ERR(c)));
400
401         struct journal_replay *p;
402         struct jset_entry *entry;
403         struct bkey_i *k, *_n;
404
405         /* This could be greatly expanded: */
406
407         list_for_each_entry(p, &c->journal_entries, list) {
408                 printf("journal entry   %8llu\n"
409                        "    version     %8u\n"
410                        "    last seq    %8llu\n"
411                        "    read clock  %8u\n"
412                        "    write clock %8u\n"
413                        ,
414                        le64_to_cpu(p->j.seq),
415                        le32_to_cpu(p->j.seq),
416                        le64_to_cpu(p->j.last_seq),
417                        le16_to_cpu(p->j.read_clock),
418                        le16_to_cpu(p->j.write_clock));
419
420                 for_each_jset_key(k, _n, entry, &p->j) {
421                         char buf[200];
422
423                         bch2_bkey_val_to_text(&PBUF(buf), c, bkey_i_to_s_c(k));
424                         printf("btree %s l %u: %s\n",
425                                bch2_btree_ids[entry->btree_id],
426                                entry->level,
427                                buf);
428                 }
429         }
430
431         bch2_fs_stop(c);
432         return 0;
433 }