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