]> git.sesse.net Git - bcachefs-tools-debian/blob - cmd_debug.c
df23ae102bfc435913dd166a28650f008e52a51e
[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 "libbcache.h"
8 #include "qcow2.h"
9 #include "tools-util.h"
10
11 #include "bcache.h"
12 #include "alloc.h"
13 #include "btree_cache.h"
14 #include "btree_iter.h"
15 #include "buckets.h"
16 #include "journal.h"
17 #include "super.h"
18
19 static void dump_usage(void)
20 {
21         puts("bcache dump - dump filesystem metadata\n"
22              "Usage: bcache dump [OPTION]... <devices>\n"
23              "\n"
24              "Options:\n"
25              "  -o output     Output qcow2 image(s)\n"
26              "  -h            Display this help and exit\n"
27              "Report bugs to <linux-bcache@vger.kernel.org>");
28 }
29
30 static void dump_one_device(struct cache_set *c, struct cache *ca, int fd)
31 {
32         struct bch_sb *sb = ca->disk_sb.sb;
33         sparse_data data;
34         unsigned i;
35
36         darray_init(data);
37
38         /* Superblock: */
39         data_add(&data, BCH_SB_LAYOUT_SECTOR << 9,
40                  sizeof(struct bch_sb_layout));
41
42         for (i = 0; i < sb->layout.nr_superblocks; i++)
43                 data_add(&data,
44                          le64_to_cpu(sb->layout.sb_offset[i]) << 9,
45                          vstruct_bytes(sb));
46
47         /* Journal: */
48         for (i = 0; i < ca->journal.nr; i++)
49                 if (ca->journal.bucket_seq[i] >= c->journal.last_seq_ondisk) {
50                         u64 bucket = ca->journal.buckets[i];
51
52                         data_add(&data,
53                                  bucket_bytes(ca) * bucket,
54                                  bucket_bytes(ca));
55                 }
56
57         /* Prios/gens: */
58         for (i = 0; i < prio_buckets(ca); i++)
59                 data_add(&data,
60                          bucket_bytes(ca) * ca->prio_last_buckets[i],
61                          bucket_bytes(ca));
62
63         /* Btree: */
64         for (i = 0; i < BTREE_ID_NR; i++) {
65                 const struct bch_extent_ptr *ptr;
66                 struct btree_iter iter;
67                 struct btree *b;
68
69                 for_each_btree_node(&iter, c, i, POS_MIN, 0, b) {
70                         struct bkey_s_c_extent e = bkey_i_to_s_c_extent(&b->key);
71
72                         extent_for_each_ptr(e, ptr)
73                                 if (ptr->dev == ca->dev_idx)
74                                         data_add(&data,
75                                                  ptr->offset << 9,
76                                                  b->written << 9);
77                 }
78                 bch_btree_iter_unlock(&iter);
79         }
80
81         qcow2_write_image(ca->disk_sb.bdev->bd_fd, fd, &data,
82                           max_t(unsigned, btree_bytes(c) / 8, block_bytes(c)));
83 }
84
85 int cmd_dump(int argc, char *argv[])
86 {
87         DECLARE_COMPLETION_ONSTACK(shutdown);
88         struct cache_set_opts opts = cache_set_opts_empty();
89         struct cache_set *c = NULL;
90         const char *err;
91         char *out = NULL, *buf;
92         unsigned i, nr_devices = 0;
93         bool force = false;
94         int fd, opt;
95
96         opts.nochanges  = true;
97         opts.noreplay   = true;
98         opts.errors     = BCH_ON_ERROR_CONTINUE;
99         fsck_err_opt    = FSCK_ERR_NO;
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         buf = alloca(strlen(out) + 10);
121         strcpy(buf, out);
122
123         err = bch_register_cache_set(argv + optind, argc - optind, opts, &c);
124         if (err)
125                 die("error opening %s: %s", argv[optind], err);
126
127         down_read(&c->gc_lock);
128
129         for (i = 0; i < c->sb.nr_devices; i++)
130                 if (c->cache[i])
131                         nr_devices++;
132
133         BUG_ON(!nr_devices);
134
135         for (i = 0; i < c->sb.nr_devices; i++) {
136                 int mode = O_WRONLY|O_CREAT|O_TRUNC;
137
138                 if (!force)
139                         mode |= O_EXCL;
140
141                 if (!c->cache[i])
142                         continue;
143
144                 if (nr_devices > 1)
145                         sprintf(buf, "%s.%u", out, i);
146
147                 fd = open(buf, mode, 0600);
148                 if (fd < 0)
149                         die("error opening %s: %s", buf, strerror(errno));
150
151                 dump_one_device(c, c->cache[i], fd);
152                 close(fd);
153         }
154
155         up_read(&c->gc_lock);
156
157         c->stop_completion = &shutdown;
158         bch_cache_set_stop(c);
159         closure_put(&c->cl);
160         wait_for_completion(&shutdown);
161         return 0;
162 }
163
164 static void list_keys(struct cache_set *c, enum btree_id btree_id,
165                       struct bpos start, struct bpos end, int mode)
166 {
167         struct btree_iter iter;
168         struct bkey_s_c k;
169         char buf[512];
170
171         for_each_btree_key(&iter, c, btree_id, start, k) {
172                 if (bkey_cmp(k.k->p, end) > 0)
173                         break;
174
175                 bch_bkey_val_to_text(c, bkey_type(0, btree_id),
176                                      buf, sizeof(buf), k);
177                 puts(buf);
178         }
179         bch_btree_iter_unlock(&iter);
180 }
181
182 static void list_btree_formats(struct cache_set *c, enum btree_id btree_id,
183                                struct bpos start, struct bpos end, int mode)
184 {
185         struct btree_iter iter;
186         struct btree *b;
187         char buf[4096];
188
189         for_each_btree_node(&iter, c, btree_id, start, 0, b) {
190                 if (bkey_cmp(b->key.k.p, end) > 0)
191                         break;
192
193                 bch_print_btree_node(c, b, buf, sizeof(buf));
194                 puts(buf);
195         }
196         bch_btree_iter_unlock(&iter);
197 }
198
199 static struct bpos parse_pos(char *buf)
200 {
201         char *s = buf;
202         char *inode     = strsep(&s, ":");
203         char *offset    = strsep(&s, ":");
204         struct bpos ret = { 0 };
205
206         if (!inode || !offset || s ||
207             kstrtoull(inode, 10, &ret.inode) ||
208             kstrtoull(offset, 10, &ret.offset))
209                 die("invalid bpos %s", buf);
210
211         return ret;
212 }
213
214 static void list_keys_usage(void)
215 {
216         puts("bcache list_keys - list filesystem metadata to stdout\n"
217              "Usage: bcache list_keys [OPTION]... <devices>\n"
218              "\n"
219              "Options:\n"
220              "  -b btree_id   Integer btree id to list\n"
221              "  -s start      Start pos (as inode:offset)\n"
222              "  -e end        End pos\n"
223              "  -m mode       Mode for listing\n"
224              "  -h            Display this help and exit\n"
225              "Report bugs to <linux-bcache@vger.kernel.org>");
226 }
227
228 int cmd_list(int argc, char *argv[])
229 {
230         DECLARE_COMPLETION_ONSTACK(shutdown);
231         struct cache_set_opts opts = cache_set_opts_empty();
232         struct cache_set *c = NULL;
233         enum btree_id btree_id = BTREE_ID_EXTENTS;
234         struct bpos start = POS_MIN, end = POS_MAX;
235         const char *err;
236         int mode = 0, opt;
237         u64 v;
238
239         opts.nochanges  = true;
240         opts.norecovery = true;
241         opts.errors     = BCH_ON_ERROR_CONTINUE;
242         fsck_err_opt    = FSCK_ERR_NO;
243
244         while ((opt = getopt(argc, argv, "b:s:e:m:h")) != -1)
245                 switch (opt) {
246                 case 'b':
247                         if (kstrtoull(optarg, 10, &v) ||
248                             v >= BTREE_ID_NR)
249                                 die("invalid btree id");
250                         btree_id = v;
251                         break;
252                 case 's':
253                         start   = parse_pos(optarg);
254                         break;
255                 case 'e':
256                         end     = parse_pos(optarg);
257                         break;
258                 case 'm':
259                         break;
260                 case 'h':
261                         list_keys_usage();
262                         exit(EXIT_SUCCESS);
263                 }
264
265         if (optind >= argc)
266                 die("Please supply device(s) to check");
267
268         err = bch_register_cache_set(argv + optind, argc - optind, opts, &c);
269         if (err)
270                 die("error opening %s: %s", argv[optind], err);
271
272         switch (mode) {
273         case 0:
274                 list_keys(c, btree_id, start, end, mode);
275                 break;
276         case 1:
277                 list_btree_formats(c, btree_id, start, end, mode);
278                 break;
279         default:
280                 die("Invalid mode");
281         }
282
283         c->stop_completion = &shutdown;
284         bch_cache_set_stop(c);
285         closure_put(&c->cl);
286         wait_for_completion(&shutdown);
287         return 0;
288 }