]> git.sesse.net Git - bcachefs-tools-debian/blob - cmd_dump.c
cmd_dump: Use buffered IO for reading device metadata
[bcachefs-tools-debian] / cmd_dump.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
10 #include "libbcachefs/bcachefs.h"
11 #include "libbcachefs/btree_cache.h"
12 #include "libbcachefs/btree_iter.h"
13 #include "libbcachefs/error.h"
14 #include "libbcachefs/extents.h"
15 #include "libbcachefs/super.h"
16
17 static void dump_usage(void)
18 {
19         puts("bcachefs dump - dump filesystem metadata\n"
20              "Usage: bcachefs dump [OPTION]... <devices>\n"
21              "\n"
22              "Options:\n"
23              "  -o output     Output qcow2 image(s)\n"
24              "  -f            Force; overwrite when needed\n"
25              "  -j            Dump entire journal, not just dirty entries\n"
26              "  -h            Display this help and exit\n"
27              "Report bugs to <linux-bcachefs@vger.kernel.org>");
28 }
29
30 static void dump_one_device(struct bch_fs *c, struct bch_dev *ca, int fd,
31                             bool entire_journal)
32 {
33         struct bch_sb *sb = ca->disk_sb.sb;
34         ranges data = { 0 };
35         unsigned i;
36         int ret;
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 (entire_journal ||
50                     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 bkey_ptrs_c ptrs;
62                 struct btree_trans trans;
63                 struct btree_iter iter;
64                 struct btree *b;
65
66                 bch2_trans_init(&trans, c, 0, 0);
67
68                 __for_each_btree_node(&trans, iter, i, POS_MIN, 0, 1, 0, b, ret) {
69                         struct btree_node_iter iter;
70                         struct bkey u;
71                         struct bkey_s_c k;
72
73                         for_each_btree_node_key_unpack(b, k, &iter, &u) {
74                                 ptrs = bch2_bkey_ptrs_c(k);
75
76                                 bkey_for_each_ptr(ptrs, ptr)
77                                         if (ptr->dev == ca->dev_idx)
78                                                 range_add(&data,
79                                                           ptr->offset << 9,
80                                                           btree_bytes(c));
81                         }
82                 }
83
84                 if (ret)
85                         die("error %s walking btree nodes", strerror(-ret));
86
87                 b = c->btree_roots[i].b;
88                 if (!btree_node_fake(b)) {
89                         ptrs = bch2_bkey_ptrs_c(bkey_i_to_s_c(&b->key));
90
91                         bkey_for_each_ptr(ptrs, ptr)
92                                 if (ptr->dev == ca->dev_idx)
93                                         range_add(&data,
94                                                   ptr->offset << 9,
95                                                   btree_bytes(c));
96                 }
97
98                 bch2_trans_iter_exit(&trans, &iter);
99                 bch2_trans_exit(&trans);
100         }
101
102         qcow2_write_image(ca->disk_sb.bdev->bd_buffered_fd, fd, &data,
103                           max_t(unsigned, btree_bytes(c) / 8, block_bytes(c)));
104         darray_exit(&data);
105 }
106
107 int cmd_dump(int argc, char *argv[])
108 {
109         struct bch_opts opts = bch2_opts_empty();
110         struct bch_dev *ca;
111         char *out = NULL;
112         unsigned i, nr_devices = 0;
113         bool force = false, entire_journal = false;
114         int fd, opt;
115
116         opt_set(opts, nochanges,        true);
117         opt_set(opts, norecovery,       true);
118         opt_set(opts, degraded,         true);
119         opt_set(opts, errors,           BCH_ON_ERROR_continue);
120         opt_set(opts, fix_errors,       FSCK_OPT_NO);
121
122         while ((opt = getopt(argc, argv, "o:fjvh")) != -1)
123                 switch (opt) {
124                 case 'o':
125                         out = optarg;
126                         break;
127                 case 'f':
128                         force = true;
129                         break;
130                 case 'j':
131                         entire_journal = true;
132                         break;
133                 case 'v':
134                         opt_set(opts, verbose, true);
135                         break;
136                 case 'h':
137                         dump_usage();
138                         exit(EXIT_SUCCESS);
139                 }
140         args_shift(optind);
141
142         if (!out)
143                 die("Please supply output filename");
144
145         if (!argc)
146                 die("Please supply device(s) to check");
147
148         struct bch_fs *c = bch2_fs_open(argv, argc, opts);
149         if (IS_ERR(c))
150                 die("error opening %s: %s", argv[0], strerror(-PTR_ERR(c)));
151
152         down_read(&c->gc_lock);
153
154         for_each_online_member(ca, c, i)
155                 nr_devices++;
156
157         BUG_ON(!nr_devices);
158
159         for_each_online_member(ca, c, i) {
160                 int flags = O_WRONLY|O_CREAT|O_TRUNC;
161
162                 if (!force)
163                         flags |= O_EXCL;
164
165                 if (!c->devs[i])
166                         continue;
167
168                 char *path = nr_devices > 1
169                         ? mprintf("%s.%u", out, i)
170                         : strdup(out);
171                 fd = xopen(path, flags, 0600);
172                 free(path);
173
174                 dump_one_device(c, ca, fd, entire_journal);
175                 close(fd);
176         }
177
178         up_read(&c->gc_lock);
179
180         bch2_fs_stop(c);
181         return 0;
182 }