]> git.sesse.net Git - bcachefs-tools-debian/blob - cmd_list.c
Rust: Start of cmd_list rewrite
[bcachefs-tools-debian] / cmd_list.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 "tools-util.h"
9
10 #include "libbcachefs/bcachefs.h"
11 #include "libbcachefs/btree_cache.h"
12 #include "libbcachefs/btree_io.h"
13 #include "libbcachefs/btree_iter.h"
14 #include "libbcachefs/checksum.h"
15 #include "libbcachefs/error.h"
16 #include "libbcachefs/extents.h"
17 #include "libbcachefs/super.h"
18
19 static void list_keys(struct bch_fs *c, enum btree_id btree_id,
20                       struct bpos start, struct bpos end)
21 {
22         struct btree_trans trans;
23         struct btree_iter iter;
24         struct bkey_s_c k;
25         struct printbuf buf = PRINTBUF;
26         int ret;
27
28         bch2_trans_init(&trans, c, 0, 0);
29
30         for_each_btree_key(&trans, iter, btree_id, start,
31                            BTREE_ITER_ALL_SNAPSHOTS|
32                            BTREE_ITER_PREFETCH, k, ret) {
33                 if (bkey_cmp(k.k->p, end) > 0)
34                         break;
35
36                 printbuf_reset(&buf);
37                 bch2_bkey_val_to_text(&buf, c, k);
38                 puts(buf.buf);
39         }
40         bch2_trans_iter_exit(&trans, &iter);
41
42         bch2_trans_exit(&trans);
43
44         printbuf_exit(&buf);
45 }
46
47 static void list_btree_formats(struct bch_fs *c, enum btree_id btree_id, unsigned level,
48                                struct bpos start, struct bpos end)
49 {
50         struct btree_trans trans;
51         struct btree_iter iter;
52         struct btree *b;
53         struct printbuf buf = PRINTBUF;
54         int ret;
55
56         bch2_trans_init(&trans, c, 0, 0);
57
58         __for_each_btree_node(&trans, iter, btree_id, start, 0, level, 0, b, ret) {
59                 if (bkey_cmp(b->key.k.p, end) > 0)
60                         break;
61
62                 printbuf_reset(&buf);
63                 bch2_btree_node_to_text(&buf, c, b);
64                 puts(buf.buf);
65         }
66         bch2_trans_iter_exit(&trans, &iter);
67
68         if (ret)
69                 die("error %s walking btree nodes", bch2_err_str(ret));
70
71         bch2_trans_exit(&trans);
72         printbuf_exit(&buf);
73 }
74
75 static void list_nodes(struct bch_fs *c, enum btree_id btree_id, unsigned level,
76                        struct bpos start, struct bpos end)
77 {
78         struct btree_trans trans;
79         struct btree_iter iter;
80         struct btree *b;
81         struct printbuf buf = PRINTBUF;
82         int ret;
83
84         bch2_trans_init(&trans, c, 0, 0);
85
86         __for_each_btree_node(&trans, iter, btree_id, start, 0, level, 0, b, ret) {
87                 if (bkey_cmp(b->key.k.p, end) > 0)
88                         break;
89
90                 printbuf_reset(&buf);
91                 bch2_bkey_val_to_text(&buf, c, bkey_i_to_s_c(&b->key));
92                 fputs(buf.buf, stdout);
93                 putchar('\n');
94         }
95         bch2_trans_iter_exit(&trans, &iter);
96
97         if (ret)
98                 die("error %s walking btree nodes", bch2_err_str(ret));
99
100         bch2_trans_exit(&trans);
101         printbuf_exit(&buf);
102 }
103
104 static void print_node_ondisk(struct bch_fs *c, struct btree *b)
105 {
106         struct btree_node *n_ondisk;
107         struct extent_ptr_decoded pick;
108         struct bch_dev *ca;
109         struct bio *bio;
110         unsigned offset = 0;
111         int ret;
112
113         if (bch2_bkey_pick_read_device(c, bkey_i_to_s_c(&b->key), NULL, &pick) <= 0) {
114                 printf("error getting device to read from\n");
115                 return;
116         }
117
118         ca = bch_dev_bkey_exists(c, pick.ptr.dev);
119         if (!bch2_dev_get_ioref(ca, READ)) {
120                 printf("error getting device to read from\n");
121                 return;
122         }
123
124         n_ondisk = aligned_alloc(block_bytes(c), btree_bytes(c));
125
126         bio = bio_alloc_bioset(ca->disk_sb.bdev,
127                                buf_pages(n_ondisk, btree_bytes(c)),
128                                REQ_OP_READ|REQ_META,
129                                GFP_NOIO,
130                                &c->btree_bio);
131         bio->bi_iter.bi_sector  = pick.ptr.offset;
132         bch2_bio_map(bio, n_ondisk, btree_bytes(c));
133
134         ret = submit_bio_wait(bio);
135         if (ret)
136                 die("error reading btree node: %i", ret);
137
138         bio_put(bio);
139         percpu_ref_put(&ca->io_ref);
140
141         while (offset < btree_sectors(c)) {
142                 struct bset *i;
143                 struct nonce nonce;
144                 struct bch_csum csum;
145                 struct bkey_packed *k;
146                 unsigned sectors;
147
148                 if (!offset) {
149                         i = &n_ondisk->keys;
150
151                         if (!bch2_checksum_type_valid(c, BSET_CSUM_TYPE(i)))
152                                 die("unknown checksum type at offset %u: %llu",
153                                     offset, BSET_CSUM_TYPE(i));
154
155                         nonce = btree_nonce(i, offset << 9);
156                         csum = csum_vstruct(c, BSET_CSUM_TYPE(i), nonce, n_ondisk);
157
158                         if (bch2_crc_cmp(csum, n_ondisk->csum))
159                                 die("invalid checksum\n");
160
161                         bset_encrypt(c, i, offset << 9);
162
163                         sectors = vstruct_sectors(n_ondisk, c->block_bits);
164                 } else {
165                         struct btree_node_entry *bne = (void *) n_ondisk + (offset << 9);
166
167                         i = &bne->keys;
168
169                         if (i->seq != n_ondisk->keys.seq)
170                                 break;
171
172                         if (!bch2_checksum_type_valid(c, BSET_CSUM_TYPE(i)))
173                                 die("unknown checksum type at offset %u: %llu",
174                                     offset, BSET_CSUM_TYPE(i));
175
176                         nonce = btree_nonce(i, offset << 9);
177                         csum = csum_vstruct(c, BSET_CSUM_TYPE(i), nonce, bne);
178
179                         if (bch2_crc_cmp(csum, bne->csum))
180                                 die("invalid checksum");
181
182                         bset_encrypt(c, i, offset << 9);
183
184                         sectors = vstruct_sectors(bne, c->block_bits);
185                 }
186
187                 fprintf(stdout, "  offset %u version %u, journal seq %llu\n",
188                         offset,
189                         le16_to_cpu(i->version),
190                         le64_to_cpu(i->journal_seq));
191                 offset += sectors;
192
193                 for (k = i->start; k != vstruct_last(i); k = bkey_next(k)) {
194                         struct bkey u;
195                         struct printbuf buf = PRINTBUF;
196
197                         printbuf_indent_add(&buf, 4);
198
199                         bch2_bkey_val_to_text(&buf, c, bkey_disassemble(b, k, &u));
200                         fprintf(stdout, "%s\n", buf.buf);
201
202                         printbuf_exit(&buf);
203                 }
204         }
205
206         free(n_ondisk);
207 }
208
209 static void list_nodes_ondisk(struct bch_fs *c, enum btree_id btree_id, unsigned level,
210                               struct bpos start, struct bpos end)
211 {
212         struct btree_trans trans;
213         struct btree_iter iter;
214         struct btree *b;
215         struct printbuf buf = PRINTBUF;
216         int ret;
217
218         bch2_trans_init(&trans, c, 0, 0);
219
220         __for_each_btree_node(&trans, iter, btree_id, start, 0, level, 0, b, ret) {
221                 if (bkey_cmp(b->key.k.p, end) > 0)
222                         break;
223
224                 printbuf_reset(&buf);
225                 bch2_bkey_val_to_text(&buf, c, bkey_i_to_s_c(&b->key));
226                 fputs(buf.buf, stdout);
227                 putchar('\n');
228
229                 print_node_ondisk(c, b);
230         }
231         bch2_trans_iter_exit(&trans, &iter);
232
233         if (ret)
234                 die("error %s walking btree nodes", bch2_err_str(ret));
235
236         bch2_trans_exit(&trans);
237         printbuf_exit(&buf);
238 }
239
240 static void list_nodes_keys(struct bch_fs *c, enum btree_id btree_id, unsigned level,
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         struct printbuf buf = PRINTBUF;
250         int ret;
251
252         bch2_trans_init(&trans, c, 0, 0);
253
254         __for_each_btree_node(&trans, iter, btree_id, start, 0, level, 0, b, ret) {
255                 if (bkey_cmp(b->key.k.p, end) > 0)
256                         break;
257
258                 printbuf_reset(&buf);
259                 bch2_btree_node_to_text(&buf, c, b);
260                 fputs(buf.buf, stdout);
261
262                 for_each_btree_node_key_unpack(b, k, &node_iter, &unpacked) {
263                         printbuf_reset(&buf);
264                         bch2_bkey_val_to_text(&buf, c, k);
265                         putchar('\t');
266                         puts(buf.buf);
267                 }
268         }
269         bch2_trans_iter_exit(&trans, &iter);
270
271         if (ret)
272                 die("error %s walking btree nodes", bch2_err_str(ret));
273
274         bch2_trans_exit(&trans);
275         printbuf_exit(&buf);
276 }
277
278 static void list_keys_usage(void)
279 {
280         puts("bcachefs list - list filesystem metadata to stdout\n"
281              "Usage: bcachefs list [OPTION]... <devices>\n"
282              "\n"
283              "Options:\n"
284              "  -b (extents|inodes|dirents|xattrs)    Btree to list from\n"
285              "  -l level                              Btree depth to descend to (0 == leaves)\n"
286              "  -s inode:offset                       Start position to list from\n"
287              "  -e inode:offset                       End position\n"
288              "  -i inode                              List keys for a given inode number\n"
289              "  -m (keys|formats|nodes|nodes_ondisk|nodes_keys)\n"
290              "                                        List mode\n"
291              "  -f                                    Check (fsck) the filesystem first\n"
292              "  -v                                    Verbose mode\n"
293              "  -h                                    Display this help and exit\n"
294              "Report bugs to <linux-bcachefs@vger.kernel.org>");
295 }
296
297 #define LIST_MODES()            \
298         x(keys)                 \
299         x(formats)              \
300         x(nodes)                \
301         x(nodes_ondisk)         \
302         x(nodes_keys)
303
304 enum list_modes {
305 #define x(n)    LIST_MODE_##n,
306         LIST_MODES()
307 #undef x
308 };
309
310 static const char * const list_modes[] = {
311 #define x(n)    #n,
312         LIST_MODES()
313 #undef x
314         NULL
315 };
316
317 int cmd_list(int argc, char *argv[])
318 {
319         struct bch_opts opts = bch2_opts_empty();
320         enum btree_id btree_id_start    = 0;
321         enum btree_id btree_id_end      = BTREE_ID_NR;
322         enum btree_id btree_id;
323         unsigned level = 0;
324         struct bpos start = POS_MIN, end = POS_MAX;
325         u64 inum = 0;
326         int mode = 0, opt;
327
328         opt_set(opts, nochanges,        true);
329         opt_set(opts, norecovery,       true);
330         opt_set(opts, degraded,         true);
331         opt_set(opts, errors,           BCH_ON_ERROR_continue);
332
333         while ((opt = getopt(argc, argv, "b:l:s:e:i:m:fvh")) != -1)
334                 switch (opt) {
335                 case 'b':
336                         btree_id_start = read_string_list_or_die(optarg,
337                                                 bch2_btree_ids, "btree id");
338                         btree_id_end = btree_id_start + 1;
339                         break;
340                 case 'l':
341                         if (kstrtouint(optarg, 10, &level) || level >= BTREE_MAX_DEPTH)
342                                 die("invalid level");
343                         break;
344                 case 's':
345                         start   = bpos_parse(optarg);
346                         break;
347                 case 'e':
348                         end     = bpos_parse(optarg);
349                         break;
350                 case 'i':
351                         if (kstrtoull(optarg, 10, &inum))
352                                 die("invalid inode %s", optarg);
353                         start   = POS(inum, 0);
354                         end     = POS(inum + 1, 0);
355                         break;
356                 case 'm':
357                         mode = read_string_list_or_die(optarg,
358                                                 list_modes, "list mode");
359                         break;
360                 case 'f':
361                         opt_set(opts, fix_errors, FSCK_OPT_YES);
362                         opt_set(opts, norecovery, false);
363                         break;
364                 case 'v':
365                         opt_set(opts, verbose, true);
366                         break;
367                 case 'h':
368                         list_keys_usage();
369                         exit(EXIT_SUCCESS);
370                 }
371         args_shift(optind);
372
373         if (!argc)
374                 die("Please supply device(s)");
375
376         struct bch_fs *c = bch2_fs_open(argv, argc, opts);
377         if (IS_ERR(c))
378                 die("error opening %s: %s", argv[0], bch2_err_str(PTR_ERR(c)));
379
380
381         for (btree_id = btree_id_start;
382              btree_id < btree_id_end;
383              btree_id++) {
384                 switch (mode) {
385                 case LIST_MODE_keys:
386                         list_keys(c, btree_id, start, end);
387                         break;
388                 case LIST_MODE_formats:
389                         list_btree_formats(c, btree_id, level, start, end);
390                         break;
391                 case LIST_MODE_nodes:
392                         list_nodes(c, btree_id, level, start, end);
393                         break;
394                 case LIST_MODE_nodes_ondisk:
395                         list_nodes_ondisk(c, btree_id, level, start, end);
396                         break;
397                 case LIST_MODE_nodes_keys:
398                         list_nodes_keys(c, btree_id, level, start, end);
399                         break;
400                 default:
401                         die("Invalid mode");
402                 }
403         }
404
405         bch2_fs_stop(c);
406         return 0;
407 }