btrfs-progs: btrfstune: make uuid rewrite progress more verbose
[platform/upstream/btrfs-progs.git] / btrfstune.c
1 /*
2  * Copyright (C) 2008 Oracle.  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 #include <stdio.h>
20 #include <stdlib.h>
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <fcntl.h>
24 #include <unistd.h>
25 #include <dirent.h>
26 #include <uuid/uuid.h>
27
28 #include "kerncompat.h"
29 #include "ctree.h"
30 #include "disk-io.h"
31 #include "transaction.h"
32 #include "utils.h"
33 #include "volumes.h"
34
35 static char *device;
36 static int force = 0;
37
38 static int update_seeding_flag(struct btrfs_root *root, int set_flag)
39 {
40         struct btrfs_trans_handle *trans;
41         struct btrfs_super_block *disk_super;
42         u64 super_flags;
43         int ret;
44
45         disk_super = root->fs_info->super_copy;
46         super_flags = btrfs_super_flags(disk_super);
47         if (set_flag) {
48                 if (super_flags & BTRFS_SUPER_FLAG_SEEDING) {
49                         if (force)
50                                 return 0;
51                         else
52                                 fprintf(stderr, "seeding flag is already set on %s\n", device);
53                         return 1;
54                 }
55                 super_flags |= BTRFS_SUPER_FLAG_SEEDING;
56         } else {
57                 if (!(super_flags & BTRFS_SUPER_FLAG_SEEDING)) {
58                         fprintf(stderr, "seeding flag is not set on %s\n",
59                                 device);
60                         return 1;
61                 }
62                 super_flags &= ~BTRFS_SUPER_FLAG_SEEDING;
63                 fprintf(stderr, "Warning: Seeding flag cleared.\n");
64         }
65
66         trans = btrfs_start_transaction(root, 1);
67         btrfs_set_super_flags(disk_super, super_flags);
68         ret = btrfs_commit_transaction(trans, root);
69
70         return ret;
71 }
72
73 static int set_super_incompat_flags(struct btrfs_root *root, u64 flags)
74 {
75         struct btrfs_trans_handle *trans;
76         struct btrfs_super_block *disk_super;
77         u64 super_flags;
78         int ret;
79
80         disk_super = root->fs_info->super_copy;
81         super_flags = btrfs_super_incompat_flags(disk_super);
82         super_flags |= flags;
83         trans = btrfs_start_transaction(root, 1);
84         btrfs_set_super_incompat_flags(disk_super, super_flags);
85         ret = btrfs_commit_transaction(trans, root);
86
87         return ret;
88 }
89
90 static int change_header_uuid(struct btrfs_root *root, struct extent_buffer *eb)
91 {
92         struct btrfs_fs_info *fs_info = root->fs_info;
93         int same_fsid = 1;
94         int same_chunk_tree_uuid = 1;
95         int ret;
96
97         /* Check for whether we need to change fs/chunk id */
98         if (!fs_info->new_fsid && !fs_info->new_chunk_tree_uuid)
99                 return 0;
100         if (fs_info->new_fsid)
101                 same_fsid = !memcmp_extent_buffer(eb, fs_info->new_fsid,
102                                           btrfs_header_fsid(), BTRFS_FSID_SIZE);
103         if (fs_info->new_chunk_tree_uuid)
104                 same_chunk_tree_uuid =
105                         !memcmp_extent_buffer(eb, fs_info->new_chunk_tree_uuid,
106                                               btrfs_header_chunk_tree_uuid(eb),
107                                               BTRFS_UUID_SIZE);
108         if (same_fsid && same_chunk_tree_uuid)
109                 return 0;
110         if (!same_fsid)
111                 write_extent_buffer(eb, fs_info->new_fsid, btrfs_header_fsid(),
112                                     BTRFS_FSID_SIZE);
113         if (!same_chunk_tree_uuid)
114                 write_extent_buffer(eb, fs_info->new_chunk_tree_uuid,
115                                     btrfs_header_chunk_tree_uuid(eb),
116                                     BTRFS_UUID_SIZE);
117         ret = write_tree_block(NULL, root, eb);
118
119         return ret;
120 }
121
122 static int change_extents_uuid(struct btrfs_fs_info *fs_info)
123 {
124         struct btrfs_root *root = fs_info->extent_root;
125         struct btrfs_path *path;
126         struct btrfs_key key = {0, 0, 0};
127         int ret = 0;
128
129         if (!fs_info->new_fsid && !fs_info->new_chunk_tree_uuid)
130                 return 0;
131
132         path = btrfs_alloc_path();
133         if (!path)
134                 return -ENOMEM;
135
136         /*
137          * Here we don't use transaction as it will takes a lot of reserve
138          * space, and that will make a near-full btrfs unable to change uuid
139          */
140         ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
141         if (ret < 0)
142                 goto out;
143
144         while (1) {
145                 struct btrfs_extent_item *ei;
146                 struct extent_buffer *eb;
147                 u64 flags;
148                 u64 bytenr;
149
150                 btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
151                 if (key.type != BTRFS_EXTENT_ITEM_KEY &&
152                     key.type != BTRFS_METADATA_ITEM_KEY)
153                         goto next;
154                 ei = btrfs_item_ptr(path->nodes[0], path->slots[0],
155                                     struct btrfs_extent_item);
156                 flags = btrfs_extent_flags(path->nodes[0], ei);
157                 if (!(flags & BTRFS_EXTENT_FLAG_TREE_BLOCK))
158                         goto next;
159
160                 bytenr = key.objectid;
161                 eb = read_tree_block(root, bytenr, root->nodesize, 0);
162                 if (IS_ERR(eb)) {
163                         fprintf(stderr, "Failed to read tree block: %llu\n",
164                                 bytenr);
165                         ret = PTR_ERR(eb);
166                         goto out;
167                 }
168                 ret = change_header_uuid(root, eb);
169                 free_extent_buffer(eb);
170                 if (ret < 0) {
171                         fprintf(stderr, "Failed to change uuid of tree block: %llu\n",
172                                 bytenr);
173                         goto out;
174                 }
175 next:
176                 ret = btrfs_next_item(root, path);
177                 if (ret < 0)
178                         goto out;
179                 if (ret > 0) {
180                         ret = 0;
181                         goto out;
182                 }
183         }
184
185 out:
186         btrfs_free_path(path);
187         return ret;
188 }
189
190 static int change_device_uuid(struct btrfs_root *root, struct extent_buffer *eb,
191                               int slot)
192 {
193         struct btrfs_fs_info *fs_info = root->fs_info;
194         struct btrfs_dev_item *di;
195         int ret = 0;
196
197         di = btrfs_item_ptr(eb, slot, struct btrfs_dev_item);
198         if (fs_info->new_fsid) {
199                 if (!memcmp_extent_buffer(eb, fs_info->new_fsid,
200                                           (unsigned long)btrfs_device_fsid(di),
201                                           BTRFS_FSID_SIZE))
202                         return ret;
203                 write_extent_buffer(eb, fs_info->new_fsid,
204                                     (unsigned long)btrfs_device_fsid(di),
205                                     BTRFS_FSID_SIZE);
206                 ret = write_tree_block(NULL, root, eb);
207         }
208         return ret;
209 }
210
211 static int change_devices_uuid(struct btrfs_fs_info *fs_info)
212 {
213         struct btrfs_root *root = fs_info->chunk_root;
214         struct btrfs_path *path;
215         struct btrfs_key key = {0, 0, 0};
216         int ret = 0;
217
218         /*
219          * Unlike change_extents_uuid, we only need to change fsid in dev_item
220          */
221         if (!fs_info->new_fsid)
222                 return 0;
223
224         path = btrfs_alloc_path();
225         if (!path)
226                 return -ENOMEM;
227         /* No transaction again */
228         ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
229         if (ret < 0)
230                 goto out;
231
232         while (1) {
233                 btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
234                 if (key.type != BTRFS_DEV_ITEM_KEY ||
235                     key.objectid != BTRFS_DEV_ITEMS_OBJECTID)
236                         goto next;
237                 ret = change_device_uuid(root, path->nodes[0], path->slots[0]);
238                 if (ret < 0)
239                         goto out;
240 next:
241                 ret = btrfs_next_item(root, path);
242                 if (ret < 0)
243                         goto out;
244                 if (ret > 0) {
245                         ret = 0;
246                         goto out;
247                 }
248         }
249 out:
250         btrfs_free_path(path);
251         return ret;
252 }
253
254 static int change_fsid_prepare(struct btrfs_fs_info *fs_info)
255 {
256         struct btrfs_root *tree_root = fs_info->tree_root;
257         u64 flags = btrfs_super_flags(fs_info->super_copy);
258         int ret = 0;
259
260         if (!fs_info->new_fsid && !fs_info->new_chunk_tree_uuid)
261                 return 0;
262
263         if (fs_info->new_fsid)
264                 flags |= BTRFS_SUPER_FLAG_CHANGING_FSID;
265         btrfs_set_super_flags(fs_info->super_copy, flags);
266
267         memcpy(fs_info->super_copy->fsid, fs_info->new_fsid, BTRFS_FSID_SIZE);
268         ret = write_all_supers(tree_root);
269         if (ret < 0)
270                 return ret;
271
272         /* also restore new chunk_tree_id into tree_root for restore */
273         write_extent_buffer(tree_root->node, fs_info->new_chunk_tree_uuid,
274                             btrfs_header_chunk_tree_uuid(tree_root->node),
275                             BTRFS_UUID_SIZE);
276         return write_tree_block(NULL, tree_root, tree_root->node);
277 }
278
279 static int change_fsid_done(struct btrfs_fs_info *fs_info)
280 {
281         u64 flags = btrfs_super_flags(fs_info->super_copy);
282
283         if (!fs_info->new_fsid && !fs_info->new_chunk_tree_uuid)
284                 return 0;
285
286         if (fs_info->new_fsid)
287                 flags &= ~BTRFS_SUPER_FLAG_CHANGING_FSID;
288         btrfs_set_super_flags(fs_info->super_copy, flags);
289
290         return write_all_supers(fs_info->tree_root);
291 }
292
293 /*
294  * Return 0 for no unfinished fsid change.
295  * Return >0 for unfinished fsid change, and restore unfinished fsid/
296  * chunk_tree_id into fsid_ret/chunk_id_ret.
297  */
298 static int check_unfinished_fsid_change(struct btrfs_fs_info *fs_info,
299                                         uuid_t fsid_ret, uuid_t chunk_id_ret)
300 {
301         struct btrfs_root *tree_root = fs_info->tree_root;
302         u64 flags = btrfs_super_flags(fs_info->super_copy);
303
304         if (flags & BTRFS_SUPER_FLAG_CHANGING_FSID) {
305                 memcpy(fsid_ret, fs_info->super_copy->fsid, BTRFS_FSID_SIZE);
306                 read_extent_buffer(tree_root->node, chunk_id_ret,
307                                 btrfs_header_chunk_tree_uuid(tree_root->node),
308                                 BTRFS_UUID_SIZE);
309                 return 1;
310         }
311         return 0;
312 }
313
314 /*
315  * Change fsid of a given fs.
316  *
317  * If new_fsid_str is not given, use a random generated UUID.
318  * Caller should check new_fsid_str is valid
319  */
320 static int change_uuid(struct btrfs_fs_info *fs_info, const char *new_fsid_str)
321 {
322         uuid_t new_fsid;
323         uuid_t new_chunk_id;
324         uuid_t old_fsid;
325         char uuid_buf[BTRFS_UUID_UNPARSED_SIZE];
326         int ret = 0;
327
328         if (check_unfinished_fsid_change(fs_info, new_fsid, new_chunk_id)) {
329                 if (new_fsid_str) {
330                         uuid_t tmp;
331
332                         uuid_parse(new_fsid_str, tmp);
333                         if (memcmp(tmp, new_fsid, BTRFS_FSID_SIZE)) {
334                                 fprintf(stderr,
335                 "ERROR: New fsid %s is not the same with unfinished fsid change\n",
336                                         new_fsid_str);
337                                 return -EINVAL;
338                         }
339                 }
340         } else {
341                 if (new_fsid_str)
342                         uuid_parse(new_fsid_str, new_fsid);
343                 else
344                         uuid_generate(new_fsid);
345
346                 uuid_generate(new_chunk_id);
347         }
348         fs_info->new_fsid = new_fsid;
349         fs_info->new_chunk_tree_uuid = new_chunk_id;
350
351         uuid_parse((const char*)fs_info->fsid, old_fsid);
352         uuid_unparse_upper(old_fsid, uuid_buf);
353         printf("Current fsid: %s\n", uuid_buf);
354
355         uuid_unparse_upper(new_fsid, uuid_buf);
356         printf("New fsid: %s\n", uuid_buf);
357         /* Now we can begin fsid change */
358         printf("Set superblock flag CHANGING_FSID\n");
359         ret = change_fsid_prepare(fs_info);
360         if (ret < 0)
361                 goto out;
362
363         /* Change extents first */
364         printf("Change fsid in extents\n");
365         ret = change_extents_uuid(fs_info);
366         if (ret < 0) {
367                 fprintf(stderr, "Failed to change UUID of metadata\n");
368                 goto out;
369         }
370
371         /* Then devices */
372         printf("Change fsid on devices\n");
373         ret = change_devices_uuid(fs_info);
374         if (ret < 0) {
375                 fprintf(stderr, "Failed to change UUID of devices\n");
376                 goto out;
377         }
378
379         /* Last, change fsid in super */
380         memcpy(fs_info->fs_devices->fsid, fs_info->new_fsid,
381                BTRFS_FSID_SIZE);
382         memcpy(fs_info->super_copy->fsid, fs_info->new_fsid,
383                BTRFS_FSID_SIZE);
384         ret = write_all_supers(fs_info->tree_root);
385         if (ret < 0)
386                 goto out;
387
388         /* Now fsid change is done */
389         printf("Clear superblock flag CHANGING_FSID\n");
390         ret = change_fsid_done(fs_info);
391         fs_info->new_fsid = NULL;
392         fs_info->new_chunk_tree_uuid = NULL;
393         printf("Fsid change finished\n");
394 out:
395         return ret;
396 }
397
398 static void print_usage(void)
399 {
400         fprintf(stderr, "usage: btrfstune [options] device\n");
401         fprintf(stderr, "\t-S value\tpositive value will enable seeding, zero to disable, negative is not allowed\n");
402         fprintf(stderr, "\t-r \t\tenable extended inode refs\n");
403         fprintf(stderr, "\t-x \t\tenable skinny metadata extent refs\n");
404         fprintf(stderr, "\t-n \t\tenable no-holes feature (more efficient sparse file representation)\n");
405         fprintf(stderr, "\t-f \t\tforce to do dangerous operation, make sure that you are aware of the dangers\n");
406         fprintf(stderr, "\t-u \t\tchange fsid, use a random one\n");
407         fprintf(stderr, "\t-U UUID\t\tchange fsid to UUID\n");
408 }
409
410 int main(int argc, char *argv[])
411 {
412         struct btrfs_root *root;
413         enum btrfs_open_ctree_flags ctree_flags = OPEN_CTREE_WRITES;
414         int success = 0;
415         int total = 0;
416         int seeding_flag = 0;
417         u64 seeding_value = 0;
418         int random_fsid = 0;
419         char *new_fsid_str = NULL;
420         int ret;
421         u64 super_flags = 0;
422
423         optind = 1;
424         while(1) {
425                 int c = getopt(argc, argv, "S:rxfuU:n");
426                 if (c < 0)
427                         break;
428                 switch(c) {
429                 case 'S':
430                         seeding_flag = 1;
431                         seeding_value = arg_strtou64(optarg);
432                         break;
433                 case 'r':
434                         super_flags |= BTRFS_FEATURE_INCOMPAT_EXTENDED_IREF;
435                         break;
436                 case 'x':
437                         super_flags |= BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA;
438                         break;
439                 case 'n':
440                         super_flags |= BTRFS_FEATURE_INCOMPAT_NO_HOLES;
441                         break;
442                 case 'f':
443                         force = 1;
444                         break;
445                 case 'U':
446                         ctree_flags |= OPEN_CTREE_IGNORE_FSID_MISMATCH;
447                         new_fsid_str = optarg;
448                         break;
449                 case 'u':
450                         ctree_flags |= OPEN_CTREE_IGNORE_FSID_MISMATCH;
451                         random_fsid = 1;
452                         break;
453                 default:
454                         print_usage();
455                         return 1;
456                 }
457         }
458
459         set_argv0(argv);
460         argc = argc - optind;
461         device = argv[optind];
462         if (check_argc_exact(argc, 1)) {
463                 print_usage();
464                 return 1;
465         }
466
467         if (random_fsid && new_fsid_str) {
468                 fprintf(stderr,
469                         "ERROR: Random fsid can't be used with specified fsid\n");
470                 return 1;
471         }
472         if (!super_flags && !seeding_flag && !(random_fsid || new_fsid_str)) {
473                 fprintf(stderr,
474                         "ERROR: At least one option should be assigned.\n");
475                 print_usage();
476                 return 1;
477         }
478
479         if (new_fsid_str) {
480                 uuid_t tmp;
481
482                 ret = uuid_parse(new_fsid_str, tmp);
483                 if (ret < 0) {
484                         fprintf(stderr,
485                                 "ERROR: Could not parse UUID: %s\n",
486                                 new_fsid_str);
487                         return 1;
488                 }
489                 if (!test_uuid_unique(new_fsid_str)) {
490                         fprintf(stderr,
491                                 "ERROR: Fsid %s is not unique\n",
492                                 new_fsid_str);
493                         return 1;
494                 }
495         }
496
497         ret = check_mounted(device);
498         if (ret < 0) {
499                 fprintf(stderr, "Could not check mount status: %s\n",
500                         strerror(-ret));
501                 return 1;
502         } else if (ret) {
503                 fprintf(stderr, "%s is mounted\n", device);
504                 return 1;
505         }
506
507         root = open_ctree(device, 0, ctree_flags);
508
509         if (!root) {
510                 fprintf(stderr, "Open ctree failed\n");
511                 return 1;
512         }
513
514         if (seeding_flag) {
515                 if (!seeding_value && !force) {
516                         fprintf(stderr, "Warning: This is dangerous, clearing the seeding flag may cause the derived device not to be mountable!\n");
517                         ret = ask_user("We are going to clear the seeding flag, are you sure?");
518                         if (!ret) {
519                                 fprintf(stderr, "Clear seeding flag canceled\n");
520                                 ret = 1;
521                                 goto out;
522                         }
523                 }
524
525                 ret = update_seeding_flag(root, seeding_value);
526                 if (!ret)
527                         success++;
528                 total++;
529         }
530
531         if (super_flags) {
532                 ret = set_super_incompat_flags(root, super_flags);
533                 if (!ret)
534                         success++;
535                 total++;
536         }
537
538         if (random_fsid || new_fsid_str) {
539                 if (!force) {
540                         fprintf(stderr,
541                                 "Warning: It's highly recommended to run 'btrfs check' before this operation\n");
542                         fprintf(stderr,
543                                 "Also canceling running UUID change progress may cause corruption\n");
544                         ret = ask_user("We are going to change UUID, are your sure?");
545                         if (!ret) {
546                                 fprintf(stderr, "UUID change canceled\n");
547                                 ret = 1;
548                                 goto out;
549                         }
550                 }
551                 ret = change_uuid(root->fs_info, new_fsid_str);
552                 if (!ret)
553                         success++;
554                 total++;
555         }
556
557         if (success == total) {
558                 ret = 0;
559         } else {
560                 root->fs_info->readonly = 1;
561                 ret = 1;
562                 fprintf(stderr, "btrfstune failed\n");
563         }
564 out:
565         close_ctree(root);
566
567         return ret;
568 }