]> git.sesse.net Git - bcachefs-tools-debian/blob - cmd_debug.c
a8c534ba505768710ae5ccd3d02e460acca64e31
[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 "journal.h"
17 #include "super.h"
18
19 static void dump_usage(void)
20 {
21         puts("bcachefs dump - dump filesystem metadata\n"
22              "Usage: bcachefs 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 bch_fs *c, struct bch_dev *ca, int fd)
31 {
32         struct bch_sb *sb = ca->disk_sb.sb;
33         ranges data;
34         unsigned i;
35
36         darray_init(data);
37
38         /* Superblock: */
39         range_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                 range_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                         range_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                 range_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                                         range_add(&data,
75                                                   ptr->offset << 9,
76                                                   b->written << 9);
77                 }
78                 bch2_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         struct bch_opts opts = bch2_opts_empty();
88         struct bch_fs *c = NULL;
89         const char *err;
90         char *out = NULL;
91         unsigned i, nr_devices = 0;
92         bool force = false;
93         int fd, opt;
94
95         opts.nochanges  = true;
96         opts.noreplay   = true;
97         opts.errors     = BCH_ON_ERROR_CONTINUE;
98         fsck_err_opt    = FSCK_ERR_NO;
99
100         while ((opt = getopt(argc, argv, "o:fh")) != -1)
101                 switch (opt) {
102                 case 'o':
103                         out = optarg;
104                         break;
105                 case 'f':
106                         force = true;
107                         break;
108                 case 'h':
109                         dump_usage();
110                         exit(EXIT_SUCCESS);
111                 }
112
113         if (optind >= argc)
114                 die("Please supply device(s) to check");
115
116         if (!out)
117                 die("Please supply output filename");
118
119         err = bch2_fs_open(argv + optind, argc - optind, opts, &c);
120         if (err)
121                 die("error opening %s: %s", argv[optind], err);
122
123         down_read(&c->gc_lock);
124
125         for (i = 0; i < c->sb.nr_devices; i++)
126                 if (c->devs[i])
127                         nr_devices++;
128
129         BUG_ON(!nr_devices);
130
131         for (i = 0; i < c->sb.nr_devices; i++) {
132                 int mode = O_WRONLY|O_CREAT|O_TRUNC;
133
134                 if (!force)
135                         mode |= 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, mode, 0600);
144                 free(path);
145
146                 dump_one_device(c, c->devs[i], 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, int mode)
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, k) {
164                 if (bkey_cmp(k.k->p, end) > 0)
165                         break;
166
167                 bch2_bkey_val_to_text(c, bkey_type(0, btree_id),
168                                       buf, sizeof(buf), k);
169                 puts(buf);
170         }
171         bch2_btree_iter_unlock(&iter);
172 }
173
174 static void list_btree_formats(struct bch_fs *c, enum btree_id btree_id,
175                                struct bpos start, struct bpos end, int mode)
176 {
177         struct btree_iter iter;
178         struct btree *b;
179         char buf[4096];
180
181         for_each_btree_node(&iter, c, btree_id, start, 0, b) {
182                 if (bkey_cmp(b->key.k.p, end) > 0)
183                         break;
184
185                 bch2_print_btree_node(c, b, buf, sizeof(buf));
186                 puts(buf);
187         }
188         bch2_btree_iter_unlock(&iter);
189 }
190
191 static struct bpos parse_pos(char *buf)
192 {
193         char *s = buf;
194         char *inode     = strsep(&s, ":");
195         char *offset    = strsep(&s, ":");
196         struct bpos ret = { 0 };
197
198         if (!inode || !offset || s ||
199             kstrtoull(inode, 10, &ret.inode) ||
200             kstrtoull(offset, 10, &ret.offset))
201                 die("invalid bpos %s", buf);
202
203         return ret;
204 }
205
206 static void list_keys_usage(void)
207 {
208         puts("bcachefs list_keys - list filesystem metadata to stdout\n"
209              "Usage: bcachefs list_keys [OPTION]... <devices>\n"
210              "\n"
211              "Options:\n"
212              "  -b (extents|inodes|dirents|xattrs)    Btree to list from\n"
213              "  -s inode:offset                       Start position to list from\n"
214              "  -e inode:offset                       End position\n"
215              "  -m (keys|formats)                     List mode\n"
216              "  -h                                    Display this help and exit\n"
217              "Report bugs to <linux-bcache@vger.kernel.org>");
218 }
219
220 static const char * const list_modes[] = {
221         "keys",
222         "formats",
223         NULL
224 };
225
226 int cmd_list(int argc, char *argv[])
227 {
228         struct bch_opts opts = bch2_opts_empty();
229         struct bch_fs *c = NULL;
230         enum btree_id btree_id = BTREE_ID_EXTENTS;
231         struct bpos start = POS_MIN, end = POS_MAX;
232         const char *err;
233         int mode = 0, opt;
234
235         opts.nochanges  = true;
236         opts.norecovery = true;
237         opts.errors     = BCH_ON_ERROR_CONTINUE;
238         fsck_err_opt    = FSCK_ERR_NO;
239
240         while ((opt = getopt(argc, argv, "b:s:e:m:h")) != -1)
241                 switch (opt) {
242                 case 'b':
243                         btree_id = read_string_list_or_die(optarg,
244                                                 bch2_btree_ids, "btree id");
245                         break;
246                 case 's':
247                         start   = parse_pos(optarg);
248                         break;
249                 case 'e':
250                         end     = parse_pos(optarg);
251                         break;
252                 case 'm':
253                         mode = read_string_list_or_die(optarg,
254                                                 list_modes, "list mode");
255                         break;
256                 case 'h':
257                         list_keys_usage();
258                         exit(EXIT_SUCCESS);
259                 }
260
261         if (optind >= argc)
262                 die("Please supply device(s) to check");
263
264         err = bch2_fs_open(argv + optind, argc - optind, opts, &c);
265         if (err)
266                 die("error opening %s: %s", argv[optind], err);
267
268         switch (mode) {
269         case 0:
270                 list_keys(c, btree_id, start, end, mode);
271                 break;
272         case 1:
273                 list_btree_formats(c, btree_id, start, end, mode);
274                 break;
275         default:
276                 die("Invalid mode");
277         }
278
279         bch2_fs_stop(c);
280         return 0;
281 }