btrfs-progs: introduce a proper structure on which cli will call register-device...
[platform/upstream/btrfs-progs.git] / super-recover.c
1 /*
2  * Copyright (C) 2013 FUJITSU LIMITED.  All rights reserved.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public
6  * License v2 as published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public
14  * License along with this program; if not, write to the
15  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
16  * Boston, MA 021110-1307, USA.
17  */
18
19 #define _XOPEN_SOURCE 500
20 #define _GNU_SOURCE 1
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <fcntl.h>
25 #include <sys/stat.h>
26 #include <uuid/uuid.h>
27 #include <errno.h>
28 #include <unistd.h>
29 #include <ctype.h>
30 #include <getopt.h>
31
32 #include "kerncompat.h"
33 #include "ctree.h"
34 #include "disk-io.h"
35 #include "list.h"
36 #include "utils.h"
37 #include "crc32c.h"
38 #include "volumes.h"
39 #include "commands.h"
40
41 struct btrfs_recover_superblock {
42         struct btrfs_fs_devices *fs_devices;
43
44         struct list_head good_supers;
45         struct list_head bad_supers;
46
47         u64 max_generation;
48 };
49
50 struct super_block_record {
51         struct list_head list;
52
53         char *device_name;
54         struct btrfs_super_block sb;
55
56         u64 bytenr;
57 };
58
59 static
60 void init_recover_superblock(struct btrfs_recover_superblock *recover)
61 {
62         INIT_LIST_HEAD(&recover->good_supers);
63         INIT_LIST_HEAD(&recover->bad_supers);
64
65         recover->fs_devices = NULL;
66         recover->max_generation = 0;
67 }
68
69 static
70 void free_recover_superblock(struct btrfs_recover_superblock *recover)
71 {
72         struct super_block_record *record;
73
74         if (!recover->fs_devices)
75                 return;
76
77         while (!list_empty(&recover->good_supers)) {
78                 record = list_entry(recover->good_supers.next,
79                                 struct super_block_record, list);
80                 list_del_init(&record->list);
81                 free(record->device_name);
82                 free(record);
83         }
84
85         while (!list_empty(&recover->bad_supers)) {
86                 record = list_entry(recover->bad_supers.next,
87                                 struct super_block_record, list);
88                 list_del_init(&record->list);
89                 free(record->device_name);
90                 free(record);
91         }
92 }
93
94 static int check_super(u64 bytenr, struct btrfs_super_block *sb)
95 {
96         int csum_size = btrfs_super_csum_size(sb);
97         char result[csum_size];
98         u32 crc = ~(u32)0;
99
100         if (btrfs_super_bytenr(sb) != bytenr)
101                 return 0;
102         if (sb->magic != cpu_to_le64(BTRFS_MAGIC))
103                 return 0;
104
105         crc = btrfs_csum_data(NULL, (char *)sb + BTRFS_CSUM_SIZE,
106                         crc, BTRFS_SUPER_INFO_SIZE - BTRFS_CSUM_SIZE);
107         btrfs_csum_final(crc, result);
108
109         return !memcmp(sb, &result, csum_size);
110 }
111
112 static int add_superblock_record(struct btrfs_super_block *sb, char *fname,
113                         u64 bytenr, struct list_head *head)
114 {
115         struct super_block_record *record;
116
117         record = malloc(sizeof(struct super_block_record));
118         if (!record)
119                 return -ENOMEM;
120
121         record->device_name = strdup(fname);
122         if (!record->device_name) {
123                 free(record);
124                 return -ENOMEM;
125         }
126         memcpy(&record->sb, sb, sizeof(*sb));
127         record->bytenr = bytenr;
128         list_add_tail(&record->list, head);
129
130         return 0;
131 }
132
133 static int
134 read_dev_supers(char *filename, struct btrfs_recover_superblock *recover)
135 {
136         int i, ret, fd;
137         u8 buf[BTRFS_SUPER_INFO_SIZE];
138         u64 max_gen, bytenr;
139         /* just ignore errno that were set in btrfs_scan_fs_devices() */
140         errno = 0;
141
142         struct btrfs_super_block *sb = (struct btrfs_super_block *)buf;
143
144         fd = open(filename, O_RDONLY, 0666);
145         if (fd < 0)
146                 return -errno;
147
148         for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) {
149                 bytenr = btrfs_sb_offset(i);
150                 ret = pread64(fd, buf, sizeof(buf), bytenr);
151                 if (ret < sizeof(buf)) {
152                         ret = -errno;
153                         goto out;
154                 }
155                 ret = check_super(bytenr, sb);
156                 if (ret) {
157                         ret = add_superblock_record(sb, filename, bytenr,
158                                                         &recover->good_supers);
159                         if (ret)
160                                 goto out;
161                         max_gen = btrfs_super_generation(sb);
162                         if (max_gen > recover->max_generation)
163                                 recover->max_generation = max_gen;
164                 } else {
165                         ret = add_superblock_record(sb, filename, bytenr,
166                                                 &recover->bad_supers);
167                         if (ret)
168                                 goto out;
169                 }
170         }
171 out:
172         close(fd);
173         return ret;
174 }
175
176 static int read_fs_supers(struct btrfs_recover_superblock *recover)
177 {
178         struct super_block_record *record;
179         struct super_block_record *next_record;
180         struct btrfs_device *dev;
181         int ret;
182         u64 gen;
183
184         list_for_each_entry(dev, &recover->fs_devices->devices,
185                                 dev_list) {
186                 ret = read_dev_supers(dev->name, recover);
187                 if (ret)
188                         return ret;
189         }
190         list_for_each_entry_safe(record, next_record,
191                         &recover->good_supers, list) {
192                 gen = btrfs_super_generation(&record->sb);
193                 if (gen < recover->max_generation)
194                         list_move_tail(&record->list, &recover->bad_supers);
195         }
196
197         return 0;
198 }
199
200 static struct super_block_record *recover_get_good_super(
201                                 struct btrfs_recover_superblock *recover)
202 {
203         struct super_block_record *record;
204         record = list_entry(recover->good_supers.next,
205                                 struct super_block_record, list);
206         return record;
207 }
208
209 static void print_all_devices(struct list_head *devices)
210 {
211         struct btrfs_device *dev;
212
213         printf("All Devices:\n");
214         list_for_each_entry(dev, devices, dev_list) {
215                 printf("\t");
216                 printf("Device: id = %llu, name = %s\n",
217                         dev->devid, dev->name);
218         }
219         printf("\n");
220 }
221
222 static void print_super_info(struct super_block_record *record)
223 {
224         printf("\t\tdevice name = %s\n", record->device_name);
225         printf("\t\tsuperblock bytenr = %llu\n", record->bytenr);
226 }
227
228 static void print_all_supers(struct btrfs_recover_superblock *recover)
229 {
230         struct super_block_record *record;
231
232         printf("\t[All good supers]:\n");
233         list_for_each_entry(record, &recover->good_supers, list) {
234                 print_super_info(record);
235                 printf("\n");
236         }
237
238         printf("\t[All bad supers]:\n");
239         list_for_each_entry(record, &recover->bad_supers, list) {
240                 print_super_info(record);
241                 printf("\n");
242         }
243         printf("\n");
244 }
245
246 static void recover_err_str(int ret)
247 {
248         switch (ret) {
249         case 0:
250                 printf("All supers are valid, no need to recover\n");
251                 break;
252         case 1:
253                 printf("Usage or syntax errors\n");
254                 break;
255         case 2:
256                 printf("Recovered bad superblocks successful\n");
257                 break;
258         case 3:
259                 printf("Failed to recover bad superblocks\n");
260                 break;
261         case 4:
262                 printf("Aborted to recover bad superblocks\n");
263                 break;
264         default:
265                 printf("Unknown recover result\n");
266                 break;
267         }
268 }
269
270 int btrfs_recover_superblocks(const char *dname,
271                         int verbose, int yes)
272 {
273         int fd, ret;
274         struct btrfs_recover_superblock recover;
275         struct super_block_record *record;
276         struct btrfs_root *root = NULL;
277
278         fd = open(dname, O_RDONLY);
279         if (fd < 0) {
280                 fprintf(stderr, "open %s error\n", dname);
281                 return 1;
282         }
283         init_recover_superblock(&recover);
284
285         ret = btrfs_scan_fs_devices(fd, dname, &recover.fs_devices, 0, 1);
286         close(fd);
287         if (ret) {
288                 ret = 1;
289                 goto no_recover;
290         }
291
292         if (verbose)
293                 print_all_devices(&recover.fs_devices->devices);
294
295         ret = read_fs_supers(&recover);
296         if (ret) {
297                 ret = 1;
298                 goto no_recover;
299         }
300         if (verbose) {
301                 printf("Before Recovering:\n");
302                 print_all_supers(&recover);
303         }
304
305         if (list_empty(&recover.bad_supers))
306                 goto no_recover;
307
308         if (!yes) {
309                 ret = ask_user("Make sure this is a btrfs disk otherwise the tool will destroy other fs, Are you sure?");
310                 if (!ret) {
311                         ret = 4;
312                         goto no_recover;
313                 }
314         }
315         record = recover_get_good_super(&recover);
316         root = open_ctree(record->device_name, record->bytenr,
317                           OPEN_CTREE_RECOVER_SUPER | OPEN_CTREE_WRITES);
318         if (!root) {
319                 ret = 3;
320                 goto no_recover;
321         }
322         /* reset super_bytenr in order that we will rewite all supers */
323         root->fs_info->super_bytenr = BTRFS_SUPER_INFO_OFFSET;
324         ret = write_all_supers(root);
325         if (!ret)
326                 ret = 2;
327         else
328                 ret = 3;
329
330         close_ctree(root);
331 no_recover:
332         recover_err_str(ret);
333         free_recover_superblock(&recover);
334         /* check if we have freed fs_deivces in close_ctree() */
335         if (!root)
336                 btrfs_close_devices(recover.fs_devices);
337         return ret;
338 }
339