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