]> git.sesse.net Git - bcachefs-tools-debian/blob - cmd_dump.c
cmd_dump: Also set read_only
[bcachefs-tools-debian] / cmd_dump.c
1 #include <fcntl.h>
2 #include <getopt.h>
3 #include <string.h>
4 #include <sys/stat.h>
5 #include <sys/types.h>
6
7 #include "cmds.h"
8 #include "libbcachefs.h"
9 #include "qcow2.h"
10
11 #include "libbcachefs/bcachefs.h"
12 #include "libbcachefs/btree_cache.h"
13 #include "libbcachefs/btree_iter.h"
14 #include "libbcachefs/error.h"
15 #include "libbcachefs/extents.h"
16 #include "libbcachefs/sb-members.h"
17 #include "libbcachefs/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              "  -f, --force   Force; overwrite when needed\n"
27              "  --nojournal   Don't dump entire journal, just dirty entries\n"
28              "  -h, --help    Display this help and exit\n"
29              "Report bugs to <linux-bcachefs@vger.kernel.org>");
30 }
31
32 static void dump_one_device(struct bch_fs *c, struct bch_dev *ca, int fd,
33                             bool entire_journal)
34 {
35         struct bch_sb *sb = ca->disk_sb.sb;
36         ranges data = { 0 };
37         unsigned i;
38         int ret;
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 (entire_journal ||
52                     ca->journal.bucket_seq[i] >= c->journal.last_seq_ondisk) {
53                         u64 bucket = ca->journal.buckets[i];
54
55                         range_add(&data,
56                                   bucket_bytes(ca) * bucket,
57                                   bucket_bytes(ca));
58                 }
59
60         /* Btree: */
61         for (i = 0; i < BTREE_ID_NR; i++) {
62                 const struct bch_extent_ptr *ptr;
63                 struct bkey_ptrs_c ptrs;
64                 struct btree_trans trans;
65                 struct btree_iter iter;
66                 struct btree *b;
67
68                 bch2_trans_init(&trans, c, 0, 0);
69
70                 __for_each_btree_node(&trans, iter, i, POS_MIN, 0, 1, 0, b, ret) {
71                         struct btree_node_iter iter;
72                         struct bkey u;
73                         struct bkey_s_c k;
74
75                         for_each_btree_node_key_unpack(b, k, &iter, &u) {
76                                 ptrs = bch2_bkey_ptrs_c(k);
77
78                                 bkey_for_each_ptr(ptrs, ptr)
79                                         if (ptr->dev == ca->dev_idx)
80                                                 range_add(&data,
81                                                           ptr->offset << 9,
82                                                           btree_bytes(c));
83                         }
84                 }
85
86                 if (ret)
87                         die("error %s walking btree nodes", bch2_err_str(ret));
88
89                 b = bch2_btree_id_root(c, i)->b;
90                 if (!btree_node_fake(b)) {
91                         ptrs = bch2_bkey_ptrs_c(bkey_i_to_s_c(&b->key));
92
93                         bkey_for_each_ptr(ptrs, ptr)
94                                 if (ptr->dev == ca->dev_idx)
95                                         range_add(&data,
96                                                   ptr->offset << 9,
97                                                   btree_bytes(c));
98                 }
99
100                 bch2_trans_iter_exit(&trans, &iter);
101                 bch2_trans_exit(&trans);
102         }
103
104         qcow2_write_image(ca->disk_sb.bdev->bd_buffered_fd, fd, &data,
105                           max_t(unsigned, btree_bytes(c) / 8, block_bytes(c)));
106         darray_exit(&data);
107 }
108
109 int cmd_dump(int argc, char *argv[])
110 {
111         static const struct option longopts[] = {
112                 { "force",              no_argument,            NULL, 'f' },
113                 { "nojournal",          no_argument,            NULL, 'j' },
114                 { "verbose",            no_argument,            NULL, 'v' },
115                 { "help",               no_argument,            NULL, 'h' },
116                 { NULL }
117         };
118         struct bch_opts opts = bch2_opts_empty();
119         struct bch_dev *ca;
120         char *out = NULL;
121         unsigned i, nr_devices = 0;
122         bool force = false, entire_journal = true;
123         int fd, opt;
124
125         opt_set(opts, read_only,        true);
126         opt_set(opts, nochanges,        true);
127         opt_set(opts, norecovery,       true);
128         opt_set(opts, degraded,         true);
129         opt_set(opts, errors,           BCH_ON_ERROR_continue);
130         opt_set(opts, fix_errors,       FSCK_FIX_no);
131
132         while ((opt = getopt_long(argc, argv, "o:fvh",
133                                   longopts, NULL)) != -1)
134                 switch (opt) {
135                 case 'o':
136                         out = optarg;
137                         break;
138                 case 'f':
139                         force = true;
140                         break;
141                 case 'j':
142                         entire_journal = false;
143                         break;
144                 case 'v':
145                         opt_set(opts, verbose, true);
146                         break;
147                 case 'h':
148                         dump_usage();
149                         exit(EXIT_SUCCESS);
150                 }
151         args_shift(optind);
152
153         if (!out)
154                 die("Please supply output filename");
155
156         if (!argc)
157                 die("Please supply device(s) to check");
158
159         struct bch_fs *c = bch2_fs_open(argv, argc, opts);
160         if (IS_ERR(c))
161                 die("error opening %s: %s", argv[0], bch2_err_str(PTR_ERR(c)));
162
163         down_read(&c->gc_lock);
164
165         for_each_online_member(ca, c, i)
166                 nr_devices++;
167
168         BUG_ON(!nr_devices);
169
170         for_each_online_member(ca, c, i) {
171                 int flags = O_WRONLY|O_CREAT|O_TRUNC;
172
173                 if (!force)
174                         flags |= O_EXCL;
175
176                 if (!c->devs[i])
177                         continue;
178
179                 char *path = nr_devices > 1
180                         ? mprintf("%s.%u.qcow2", out, i)
181                         : mprintf("%s.qcow2", out);
182                 fd = xopen(path, flags, 0600);
183                 free(path);
184
185                 dump_one_device(c, ca, fd, entire_journal);
186                 close(fd);
187         }
188
189         up_read(&c->gc_lock);
190
191         bch2_fs_stop(c);
192         return 0;
193 }