]> git.sesse.net Git - bcachefs-tools-debian/blob - cmd_debug.c
bcachefs-in-userspace 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 "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
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
112         if (optind >= argc)
113                 die("Please supply device(s) to check");
114
115         if (!out)
116                 die("Please supply output filename");
117
118         err = bch2_fs_open(argv + optind, argc - optind, opts, &c);
119         if (err)
120                 die("error opening %s: %s", argv[optind], err);
121
122         down_read(&c->gc_lock);
123
124         for (i = 0; i < c->sb.nr_devices; i++)
125                 if (c->devs[i])
126                         nr_devices++;
127
128         BUG_ON(!nr_devices);
129
130         for (i = 0; i < c->sb.nr_devices; i++) {
131                 int mode = O_WRONLY|O_CREAT|O_TRUNC;
132
133                 if (!force)
134                         mode |= 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, mode, 0600);
143                 free(path);
144
145                 dump_one_device(c, c->devs[i], 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, int mode)
157 {
158         struct btree_iter iter;
159         struct bkey_s_c k;
160         char buf[512];
161
162         for_each_btree_key(&iter, c, btree_id, start, k) {
163                 if (bkey_cmp(k.k->p, end) > 0)
164                         break;
165
166                 bch2_bkey_val_to_text(c, bkey_type(0, btree_id),
167                                       buf, sizeof(buf), k);
168                 puts(buf);
169         }
170         bch2_btree_iter_unlock(&iter);
171 }
172
173 static void list_btree_formats(struct bch_fs *c, enum btree_id btree_id,
174                                struct bpos start, struct bpos end, int mode)
175 {
176         struct btree_iter iter;
177         struct btree *b;
178         char buf[4096];
179
180         for_each_btree_node(&iter, c, btree_id, start, 0, b) {
181                 if (bkey_cmp(b->key.k.p, end) > 0)
182                         break;
183
184                 bch2_print_btree_node(c, b, buf, sizeof(buf));
185                 puts(buf);
186         }
187         bch2_btree_iter_unlock(&iter);
188 }
189
190 static struct bpos parse_pos(char *buf)
191 {
192         char *s = buf;
193         char *inode     = strsep(&s, ":");
194         char *offset    = strsep(&s, ":");
195         struct bpos ret = { 0 };
196
197         if (!inode || !offset || s ||
198             kstrtoull(inode, 10, &ret.inode) ||
199             kstrtoull(offset, 10, &ret.offset))
200                 die("invalid bpos %s", buf);
201
202         return ret;
203 }
204
205 static void list_keys_usage(void)
206 {
207         puts("bcachefs list_keys - list filesystem metadata to stdout\n"
208              "Usage: bcachefs list_keys [OPTION]... <devices>\n"
209              "\n"
210              "Options:\n"
211              "  -b (extents|inodes|dirents|xattrs)    Btree to list from\n"
212              "  -s inode:offset                       Start position to list from\n"
213              "  -e inode:offset                       End position\n"
214              "  -m (keys|formats)                     List mode\n"
215              "  -h                                    Display this help and exit\n"
216              "Report bugs to <linux-bcache@vger.kernel.org>");
217 }
218
219 static const char * const list_modes[] = {
220         "keys",
221         "formats",
222         NULL
223 };
224
225 int cmd_list(int argc, char *argv[])
226 {
227         struct bch_opts opts = bch2_opts_empty();
228         struct bch_fs *c = NULL;
229         enum btree_id btree_id = BTREE_ID_EXTENTS;
230         struct bpos start = POS_MIN, end = POS_MAX;
231         const char *err;
232         int mode = 0, opt;
233
234         opts.nochanges  = true;
235         opts.norecovery = true;
236         opts.errors     = BCH_ON_ERROR_CONTINUE;
237
238         while ((opt = getopt(argc, argv, "b:s:e:m:h")) != -1)
239                 switch (opt) {
240                 case 'b':
241                         btree_id = read_string_list_or_die(optarg,
242                                                 bch2_btree_ids, "btree id");
243                         break;
244                 case 's':
245                         start   = parse_pos(optarg);
246                         break;
247                 case 'e':
248                         end     = parse_pos(optarg);
249                         break;
250                 case 'm':
251                         mode = read_string_list_or_die(optarg,
252                                                 list_modes, "list mode");
253                         break;
254                 case 'h':
255                         list_keys_usage();
256                         exit(EXIT_SUCCESS);
257                 }
258
259         if (optind >= argc)
260                 die("Please supply device(s) to check");
261
262         err = bch2_fs_open(argv + optind, argc - optind, opts, &c);
263         if (err)
264                 die("error opening %s: %s", argv[optind], err);
265
266         switch (mode) {
267         case 0:
268                 list_keys(c, btree_id, start, end, mode);
269                 break;
270         case 1:
271                 list_btree_formats(c, btree_id, start, end, mode);
272                 break;
273         default:
274                 die("Invalid mode");
275         }
276
277         bch2_fs_stop(c);
278         return 0;
279 }