]> git.sesse.net Git - bcachefs-tools-debian/blob - cmd_debug.c
974e862e2eab107293cdb08c72d52946c424716b
[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 "bcachefs.h"
12 #include "alloc.h"
13 #include "btree_cache.h"
14 #include "btree_iter.h"
15 #include "buckets.h"
16 #include "error.h"
17 #include "journal.h"
18 #include "super.h"
19
20 static void dump_usage(void)
21 {
22         puts("bcachefs dump - dump filesystem metadata\n"
23              "Usage: bcachefs dump [OPTION]... <devices>\n"
24              "\n"
25              "Options:\n"
26              "  -o output     Output qcow2 image(s)\n"
27              "  -h            Display this help and exit\n"
28              "Report bugs to <linux-bcache@vger.kernel.org>");
29 }
30
31 static void dump_one_device(struct bch_fs *c, struct bch_dev *ca, int fd)
32 {
33         struct bch_sb *sb = ca->disk_sb.sb;
34         ranges data;
35         unsigned i;
36
37         darray_init(data);
38
39         /* Superblock: */
40         range_add(&data, BCH_SB_LAYOUT_SECTOR << 9,
41                   sizeof(struct bch_sb_layout));
42
43         for (i = 0; i < sb->layout.nr_superblocks; i++)
44                 range_add(&data,
45                           le64_to_cpu(sb->layout.sb_offset[i]) << 9,
46                           vstruct_bytes(sb));
47
48         /* Journal: */
49         for (i = 0; i < ca->journal.nr; i++)
50                 if (ca->journal.bucket_seq[i] >= c->journal.last_seq_ondisk) {
51                         u64 bucket = ca->journal.buckets[i];
52
53                         range_add(&data,
54                                   bucket_bytes(ca) * bucket,
55                                   bucket_bytes(ca));
56                 }
57
58         /* Prios/gens: */
59         for (i = 0; i < prio_buckets(ca); i++)
60                 range_add(&data,
61                           bucket_bytes(ca) * ca->prio_last_buckets[i],
62                           bucket_bytes(ca));
63
64         /* Btree: */
65         for (i = 0; i < BTREE_ID_NR; i++) {
66                 const struct bch_extent_ptr *ptr;
67                 struct btree_iter iter;
68                 struct btree *b;
69
70                 for_each_btree_node(&iter, c, i, POS_MIN, 0, b) {
71                         struct bkey_s_c_extent e = bkey_i_to_s_c_extent(&b->key);
72
73                         extent_for_each_ptr(e, ptr)
74                                 if (ptr->dev == ca->dev_idx)
75                                         range_add(&data,
76                                                   ptr->offset << 9,
77                                                   b->written << 9);
78                 }
79                 bch2_btree_iter_unlock(&iter);
80         }
81
82         qcow2_write_image(ca->disk_sb.bdev->bd_fd, fd, &data,
83                           max_t(unsigned, btree_bytes(c) / 8, block_bytes(c)));
84 }
85
86 int cmd_dump(int argc, char *argv[])
87 {
88         struct bch_opts opts = bch2_opts_empty();
89         struct bch_fs *c = NULL;
90         struct bch_dev *ca;
91         const char *err;
92         char *out = NULL;
93         unsigned i, nr_devices = 0;
94         bool force = false;
95         int fd, opt;
96
97         opts.nochanges  = true;
98         opts.noreplay   = true;
99         opts.errors     = BCH_ON_ERROR_CONTINUE;
100
101         while ((opt = getopt(argc, argv, "o:fh")) != -1)
102                 switch (opt) {
103                 case 'o':
104                         out = optarg;
105                         break;
106                 case 'f':
107                         force = true;
108                         break;
109                 case 'h':
110                         dump_usage();
111                         exit(EXIT_SUCCESS);
112                 }
113
114         if (optind >= argc)
115                 die("Please supply device(s) to check");
116
117         if (!out)
118                 die("Please supply output filename");
119
120         err = bch2_fs_open(argv + optind, argc - optind, opts, &c);
121         if (err)
122                 die("error opening %s: %s", argv[optind], err);
123
124         down_read(&c->gc_lock);
125
126         for_each_online_member(ca, c, i)
127                 nr_devices++;
128
129         BUG_ON(!nr_devices);
130
131         for_each_online_member(ca, c, i) {
132                 int flags = O_WRONLY|O_CREAT|O_TRUNC;
133
134                 if (!force)
135                         flags |= O_EXCL;
136
137                 if (!c->devs[i])
138                         continue;
139
140                 char *path = nr_devices > 1
141                         ? mprintf("%s.%u", out, i)
142                         : strdup(out);
143                 fd = xopen(path, flags, 0600);
144                 free(path);
145
146                 dump_one_device(c, ca, fd);
147                 close(fd);
148         }
149
150         up_read(&c->gc_lock);
151
152         bch2_fs_stop(c);
153         return 0;
154 }
155
156 static void list_keys(struct bch_fs *c, enum btree_id btree_id,
157                       struct bpos start, struct bpos end)
158 {
159         struct btree_iter iter;
160         struct bkey_s_c k;
161         char buf[512];
162
163         for_each_btree_key(&iter, c, btree_id, start,
164                            BTREE_ITER_PREFETCH, k) {
165                 if (bkey_cmp(k.k->p, end) > 0)
166                         break;
167
168                 bch2_bkey_val_to_text(c, bkey_type(0, btree_id),
169                                       buf, sizeof(buf), k);
170                 puts(buf);
171         }
172         bch2_btree_iter_unlock(&iter);
173 }
174
175 static void list_btree_formats(struct bch_fs *c, enum btree_id btree_id,
176                                struct bpos start, struct bpos end)
177 {
178         struct btree_iter iter;
179         struct btree *b;
180         char buf[4096];
181
182         for_each_btree_node(&iter, c, btree_id, start, 0, b) {
183                 if (bkey_cmp(b->key.k.p, end) > 0)
184                         break;
185
186                 bch2_print_btree_node(c, b, buf, sizeof(buf));
187                 puts(buf);
188         }
189         bch2_btree_iter_unlock(&iter);
190 }
191
192 static void list_nodes_keys(struct bch_fs *c, enum btree_id btree_id,
193                             struct bpos start, struct bpos end)
194 {
195         struct btree_iter iter;
196         struct btree_node_iter node_iter;
197         struct bkey unpacked;
198         struct bkey_s_c k;
199         struct btree *b;
200         char buf[4096];
201
202         for_each_btree_node(&iter, c, btree_id, start, 0, b) {
203                 if (bkey_cmp(b->key.k.p, end) > 0)
204                         break;
205
206                 bch2_print_btree_node(c, b, buf, sizeof(buf));
207                 fputs(buf, stdout);
208
209                 buf[0] = '\t';
210
211                 for_each_btree_node_key_unpack(b, k, &node_iter,
212                                                btree_node_is_extents(b),
213                                                &unpacked) {
214                         bch2_bkey_val_to_text(c, bkey_type(0, btree_id),
215                                               buf + 1, sizeof(buf) - 1, k);
216                         puts(buf);
217                 }
218         }
219         bch2_btree_iter_unlock(&iter);
220 }
221
222 static struct bpos parse_pos(char *buf)
223 {
224         char *s = buf, *field;
225         u64 inode_v = 0, offset_v = 0;
226
227         if (!(field = strsep(&s, ":")) ||
228             kstrtoull(field, 10, &inode_v))
229                 die("invalid bpos %s", buf);
230
231         if ((field = strsep(&s, ":")) &&
232             kstrtoull(field, 10, &offset_v))
233                 die("invalid bpos %s", buf);
234
235         if (s)
236                 die("invalid bpos %s", buf);
237
238         return (struct bpos) { .inode = inode_v, .offset = offset_v };
239 }
240
241 static void list_keys_usage(void)
242 {
243         puts("bcachefs list_keys - list filesystem metadata to stdout\n"
244              "Usage: bcachefs list_keys [OPTION]... <devices>\n"
245              "\n"
246              "Options:\n"
247              "  -b (extents|inodes|dirents|xattrs)    Btree to list from\n"
248              "  -s inode:offset                       Start position to list from\n"
249              "  -e inode:offset                       End position\n"
250              "  -i inode                              List keys for a given inode number\n"
251              "  -m (keys|formats)                     List mode\n"
252              "  -h                                    Display this help and exit\n"
253              "Report bugs to <linux-bcache@vger.kernel.org>");
254 }
255
256 static const char * const list_modes[] = {
257         "keys",
258         "formats",
259         "nodes",
260         NULL
261 };
262
263 int cmd_list(int argc, char *argv[])
264 {
265         struct bch_opts opts = bch2_opts_empty();
266         struct bch_fs *c = NULL;
267         enum btree_id btree_id = BTREE_ID_EXTENTS;
268         struct bpos start = POS_MIN, end = POS_MAX;
269         const char *err;
270         u64 inum;
271         int mode = 0, opt;
272
273         opts.nochanges  = true;
274         opts.norecovery = true;
275         opts.errors     = BCH_ON_ERROR_CONTINUE;
276
277         while ((opt = getopt(argc, argv, "b:s:e:i:m:fvh")) != -1)
278                 switch (opt) {
279                 case 'b':
280                         btree_id = read_string_list_or_die(optarg,
281                                                 bch2_btree_ids, "btree id");
282                         break;
283                 case 's':
284                         start   = parse_pos(optarg);
285                         break;
286                 case 'e':
287                         end     = parse_pos(optarg);
288                         break;
289                 case 'i':
290                         if (kstrtoull(optarg, 10, &inum))
291                                 die("invalid inode %s", optarg);
292                         start   = POS(inum, 0);
293                         end     = POS(inum + 1, 0);
294                         break;
295                 case 'm':
296                         mode = read_string_list_or_die(optarg,
297                                                 list_modes, "list mode");
298                         break;
299                 case 'f':
300                         opts.fix_errors = FSCK_ERR_YES;
301                         opts.norecovery = false;
302                         break;
303                 case 'v':
304                         opts.verbose_recovery = true;
305                         break;
306                 case 'h':
307                         list_keys_usage();
308                         exit(EXIT_SUCCESS);
309                 }
310
311         if (optind >= argc)
312                 die("Please supply device(s) to check");
313
314         err = bch2_fs_open(argv + optind, argc - optind, opts, &c);
315         if (err)
316                 die("error opening %s: %s", argv[optind], err);
317
318         switch (mode) {
319         case 0:
320                 list_keys(c, btree_id, start, end);
321                 break;
322         case 1:
323                 list_btree_formats(c, btree_id, start, end);
324                 break;
325         case 2:
326                 list_nodes_keys(c, btree_id, start, end);
327                 break;
328         default:
329                 die("Invalid mode");
330         }
331
332         bch2_fs_stop(c);
333         return 0;
334 }