]> git.sesse.net Git - bcachefs-tools-debian/blob - cmd_debug.c
Update llist code
[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
98         while ((opt = getopt(argc, argv, "o:fh")) != -1)
99                 switch (opt) {
100                 case 'o':
101                         out = optarg;
102                         break;
103                 case 'f':
104                         force = true;
105                         break;
106                 case 'h':
107                         dump_usage();
108                         exit(EXIT_SUCCESS);
109                 }
110         args_shift(optind);
111
112         if (!out)
113                 die("Please supply output filename");
114
115         if (!argc)
116                 die("Please supply device(s) to check");
117
118         struct bch_fs *c = bch2_fs_open(argv, argc, opts);
119         if (IS_ERR(c))
120                 die("error opening %s: %s", argv[0], strerror(-PTR_ERR(c)));
121
122         down_read(&c->gc_lock);
123
124         for_each_online_member(ca, c, i)
125                 nr_devices++;
126
127         BUG_ON(!nr_devices);
128
129         for_each_online_member(ca, c, i) {
130                 int flags = O_WRONLY|O_CREAT|O_TRUNC;
131
132                 if (!force)
133                         flags |= O_EXCL;
134
135                 if (!c->devs[i])
136                         continue;
137
138                 char *path = nr_devices > 1
139                         ? mprintf("%s.%u", out, i)
140                         : strdup(out);
141                 fd = xopen(path, flags, 0600);
142                 free(path);
143
144                 dump_one_device(c, ca, fd);
145                 close(fd);
146         }
147
148         up_read(&c->gc_lock);
149
150         bch2_fs_stop(c);
151         return 0;
152 }
153
154 static void list_keys(struct bch_fs *c, enum btree_id btree_id,
155                       struct bpos start, struct bpos end)
156 {
157         struct btree_trans trans;
158         struct btree_iter *iter;
159         struct bkey_s_c k;
160         char buf[512];
161         int ret;
162
163         bch2_trans_init(&trans, c, 0, 0);
164
165         for_each_btree_key(&trans, iter, btree_id, start,
166                            BTREE_ITER_PREFETCH, k, ret) {
167                 if (bkey_cmp(k.k->p, end) > 0)
168                         break;
169
170                 bch2_bkey_val_to_text(&PBUF(buf), c, k);
171                 puts(buf);
172         }
173         bch2_trans_exit(&trans);
174 }
175
176 static void list_btree_formats(struct bch_fs *c, enum btree_id btree_id,
177                                struct bpos start, struct bpos end)
178 {
179         struct btree_trans trans;
180         struct btree_iter *iter;
181         struct btree *b;
182         char buf[4096];
183
184         bch2_trans_init(&trans, c, 0, 0);
185
186         for_each_btree_node(&trans, iter, btree_id, start, 0, b) {
187                 if (bkey_cmp(b->key.k.p, end) > 0)
188                         break;
189
190                 bch2_btree_node_to_text(&PBUF(buf), c, b);
191                 puts(buf);
192         }
193         bch2_trans_exit(&trans);
194 }
195
196 static void list_nodes_keys(struct bch_fs *c, enum btree_id btree_id,
197                             struct bpos start, struct bpos end)
198 {
199         struct btree_trans trans;
200         struct btree_iter *iter;
201         struct btree_node_iter node_iter;
202         struct bkey unpacked;
203         struct bkey_s_c k;
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                 fputs(buf, stdout);
215
216                 for_each_btree_node_key_unpack(b, k, &node_iter, &unpacked) {
217                         bch2_bkey_val_to_text(&PBUF(buf), c, k);
218                         putchar('\t');
219                         puts(buf);
220                 }
221         }
222         bch2_trans_exit(&trans);
223 }
224
225 static struct bpos parse_pos(char *buf)
226 {
227         char *s = buf, *field;
228         u64 inode_v = 0, offset_v = 0;
229
230         if (!(field = strsep(&s, ":")) ||
231             kstrtoull(field, 10, &inode_v))
232                 die("invalid bpos %s", buf);
233
234         if ((field = strsep(&s, ":")) &&
235             kstrtoull(field, 10, &offset_v))
236                 die("invalid bpos %s", buf);
237
238         if (s)
239                 die("invalid bpos %s", buf);
240
241         return (struct bpos) { .inode = inode_v, .offset = offset_v };
242 }
243
244 static void list_keys_usage(void)
245 {
246         puts("bcachefs list - list filesystem metadata to stdout\n"
247              "Usage: bcachefs list [OPTION]... <devices>\n"
248              "\n"
249              "Options:\n"
250              "  -b (extents|inodes|dirents|xattrs)    Btree to list from\n"
251              "  -s inode:offset                       Start position to list from\n"
252              "  -e inode:offset                       End position\n"
253              "  -i inode                              List keys for a given inode number\n"
254              "  -m (keys|formats)                     List mode\n"
255              "  -f                                    Check (fsck) the filesystem first\n"
256              "  -v                                    Verbose mode\n"
257              "  -h                                    Display this help and exit\n"
258              "Report bugs to <linux-bcache@vger.kernel.org>");
259 }
260
261 static const char * const list_modes[] = {
262         "keys",
263         "formats",
264         "nodes",
265         NULL
266 };
267
268 int cmd_list(int argc, char *argv[])
269 {
270         struct bch_opts opts = bch2_opts_empty();
271         enum btree_id btree_id = BTREE_ID_EXTENTS;
272         struct bpos start = POS_MIN, end = POS_MAX;
273         u64 inum;
274         int mode = 0, opt;
275
276         opt_set(opts, nochanges,        true);
277         opt_set(opts, norecovery,       true);
278         opt_set(opts, degraded,         true);
279         opt_set(opts, errors,           BCH_ON_ERROR_CONTINUE);
280
281         while ((opt = getopt(argc, argv, "b:s:e:i:m:fvh")) != -1)
282                 switch (opt) {
283                 case 'b':
284                         btree_id = read_string_list_or_die(optarg,
285                                                 bch2_btree_ids, "btree id");
286                         break;
287                 case 's':
288                         start   = parse_pos(optarg);
289                         break;
290                 case 'e':
291                         end     = parse_pos(optarg);
292                         break;
293                 case 'i':
294                         if (kstrtoull(optarg, 10, &inum))
295                                 die("invalid inode %s", optarg);
296                         start   = POS(inum, 0);
297                         end     = POS(inum + 1, 0);
298                         break;
299                 case 'm':
300                         mode = read_string_list_or_die(optarg,
301                                                 list_modes, "list mode");
302                         break;
303                 case 'f':
304                         opt_set(opts, fix_errors, FSCK_OPT_YES);
305                         opt_set(opts, norecovery, false);
306                         break;
307                 case 'v':
308                         opt_set(opts, verbose, true);
309                         break;
310                 case 'h':
311                         list_keys_usage();
312                         exit(EXIT_SUCCESS);
313                 }
314         args_shift(optind);
315
316         if (!argc)
317                 die("Please supply device(s)");
318
319         struct bch_fs *c = bch2_fs_open(argv, argc, opts);
320         if (IS_ERR(c))
321                 die("error opening %s: %s", argv[0], strerror(-PTR_ERR(c)));
322
323         switch (mode) {
324         case 0:
325                 list_keys(c, btree_id, start, end);
326                 break;
327         case 1:
328                 list_btree_formats(c, btree_id, start, end);
329                 break;
330         case 2:
331                 list_nodes_keys(c, btree_id, start, end);
332                 break;
333         default:
334                 die("Invalid mode");
335         }
336
337         bch2_fs_stop(c);
338         return 0;
339 }