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