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