]> git.sesse.net Git - bcachefs-tools-debian/blob - cmd_debug.c
check if fs is mounted before running fsck
[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/bset.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(&PBUF(buf), c, 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_btree_node_to_text(&PBUF(buf), c, b);
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_btree_node_to_text(&PBUF(buf), c, b);
201                 fputs(buf, stdout);
202
203                 for_each_btree_node_key_unpack(b, k, &node_iter, &unpacked) {
204                         bch2_bkey_val_to_text(&PBUF(buf), c, k);
205                         putchar('\t');
206                         puts(buf);
207                 }
208         }
209         bch2_btree_iter_unlock(&iter);
210 }
211
212 static struct bpos parse_pos(char *buf)
213 {
214         char *s = buf, *field;
215         u64 inode_v = 0, offset_v = 0;
216
217         if (!(field = strsep(&s, ":")) ||
218             kstrtoull(field, 10, &inode_v))
219                 die("invalid bpos %s", buf);
220
221         if ((field = strsep(&s, ":")) &&
222             kstrtoull(field, 10, &offset_v))
223                 die("invalid bpos %s", buf);
224
225         if (s)
226                 die("invalid bpos %s", buf);
227
228         return (struct bpos) { .inode = inode_v, .offset = offset_v };
229 }
230
231 static void list_keys_usage(void)
232 {
233         puts("bcachefs list - list filesystem metadata to stdout\n"
234              "Usage: bcachefs list [OPTION]... <devices>\n"
235              "\n"
236              "Options:\n"
237              "  -b (extents|inodes|dirents|xattrs)    Btree to list from\n"
238              "  -s inode:offset                       Start position to list from\n"
239              "  -e inode:offset                       End position\n"
240              "  -i inode                              List keys for a given inode number\n"
241              "  -m (keys|formats)                     List mode\n"
242              "  -f                                    Check (fsck) the filesystem first\n"
243              "  -v                                    Verbose mode\n"
244              "  -h                                    Display this help and exit\n"
245              "Report bugs to <linux-bcache@vger.kernel.org>");
246 }
247
248 static const char * const list_modes[] = {
249         "keys",
250         "formats",
251         "nodes",
252         NULL
253 };
254
255 int cmd_list(int argc, char *argv[])
256 {
257         struct bch_opts opts = bch2_opts_empty();
258         enum btree_id btree_id = BTREE_ID_EXTENTS;
259         struct bpos start = POS_MIN, end = POS_MAX;
260         u64 inum;
261         int mode = 0, opt;
262
263         opt_set(opts, nochanges,        true);
264         opt_set(opts, norecovery,       true);
265         opt_set(opts, degraded,         true);
266         opt_set(opts, errors,           BCH_ON_ERROR_CONTINUE);
267
268         while ((opt = getopt(argc, argv, "b:s:e:i:m:fvh")) != -1)
269                 switch (opt) {
270                 case 'b':
271                         btree_id = read_string_list_or_die(optarg,
272                                                 bch2_btree_ids, "btree id");
273                         break;
274                 case 's':
275                         start   = parse_pos(optarg);
276                         break;
277                 case 'e':
278                         end     = parse_pos(optarg);
279                         break;
280                 case 'i':
281                         if (kstrtoull(optarg, 10, &inum))
282                                 die("invalid inode %s", optarg);
283                         start   = POS(inum, 0);
284                         end     = POS(inum + 1, 0);
285                         break;
286                 case 'm':
287                         mode = read_string_list_or_die(optarg,
288                                                 list_modes, "list mode");
289                         break;
290                 case 'f':
291                         opt_set(opts, fix_errors, FSCK_OPT_YES);
292                         opt_set(opts, norecovery, false);
293                         break;
294                 case 'v':
295                         opt_set(opts, verbose, true);
296                         break;
297                 case 'h':
298                         list_keys_usage();
299                         exit(EXIT_SUCCESS);
300                 }
301         args_shift(optind);
302
303         if (!argc)
304                 die("Please supply device(s)");
305
306         struct bch_fs *c = bch2_fs_open(argv, argc, opts);
307         if (IS_ERR(c))
308                 die("error opening %s: %s", argv[0], strerror(-PTR_ERR(c)));
309
310         switch (mode) {
311         case 0:
312                 list_keys(c, btree_id, start, end);
313                 break;
314         case 1:
315                 list_btree_formats(c, btree_id, start, end);
316                 break;
317         case 2:
318                 list_nodes_keys(c, btree_id, start, end);
319                 break;
320         default:
321                 die("Invalid mode");
322         }
323
324         bch2_fs_stop(c);
325         return 0;
326 }