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