3183631361a376407e47d9a1958ffee8dda5bf32
[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         char uuid_buf[BTRFS_UUID_UNPARSED_SIZE];
325         int ret = 0;
326
327         if (check_unfinished_fsid_change(fs_info, new_fsid, new_chunk_id)) {
328                 if (new_fsid_str) {
329                         uuid_t tmp;
330
331                         uuid_parse(new_fsid_str, tmp);
332                         if (memcmp(tmp, new_fsid, BTRFS_FSID_SIZE)) {
333                                 fprintf(stderr,
334                 "ERROR: New fsid %s is not the same with unfinished fsid change\n",
335                                         new_fsid_str);
336                                 return -EINVAL;
337                         }
338                 }
339         } else {
340                 if (new_fsid_str)
341                         uuid_parse(new_fsid_str, new_fsid);
342                 else
343                         uuid_generate(new_fsid);
344
345                 uuid_generate(new_chunk_id);
346         }
347         fs_info->new_fsid = new_fsid;
348         fs_info->new_chunk_tree_uuid = new_chunk_id;
349
350         uuid_unparse_upper(new_fsid, uuid_buf);
351         printf("Changing fsid to %s\n", uuid_buf);
352         /* Now we can begin fsid change */
353         ret = change_fsid_prepare(fs_info);
354         if (ret < 0)
355                 goto out;
356
357         /* Change extents first */
358         ret = change_extents_uuid(fs_info);
359         if (ret < 0) {
360                 fprintf(stderr, "Failed to change UUID of metadata\n");
361                 goto out;
362         }
363
364         /* Then devices */
365         ret = change_devices_uuid(fs_info);
366         if (ret < 0) {
367                 fprintf(stderr, "Failed to change UUID of devices\n");
368                 goto out;
369         }
370
371         /* Last, change fsid in super */
372         memcpy(fs_info->fs_devices->fsid, fs_info->new_fsid,
373                BTRFS_FSID_SIZE);
374         memcpy(fs_info->super_copy->fsid, fs_info->new_fsid,
375                BTRFS_FSID_SIZE);
376         ret = write_all_supers(fs_info->tree_root);
377         if (ret < 0)
378                 goto out;
379
380         /* Now fsid change is done */
381         ret = change_fsid_done(fs_info);
382         fs_info->new_fsid = NULL;
383         fs_info->new_chunk_tree_uuid = NULL;
384         printf("Fsid changed to %s\n", uuid_buf);
385 out:
386         return ret;
387 }
388
389 static void print_usage(void)
390 {
391         fprintf(stderr, "usage: btrfstune [options] device\n");
392         fprintf(stderr, "\t-S value\tpositive value will enable seeding, zero to disable, negative is not allowed\n");
393         fprintf(stderr, "\t-r \t\tenable extended inode refs\n");
394         fprintf(stderr, "\t-x \t\tenable skinny metadata extent refs\n");
395         fprintf(stderr, "\t-f \t\tforce to do dangerous operation, make sure that you are aware of the dangers\n");
396         fprintf(stderr, "\t-u \t\tchange fsid, use a random one\n");
397         fprintf(stderr, "\t-U UUID\t\tchange fsid to UUID\n");
398 }
399
400 int main(int argc, char *argv[])
401 {
402         struct btrfs_root *root;
403         enum btrfs_open_ctree_flags ctree_flags = OPEN_CTREE_WRITES;
404         int success = 0;
405         int total = 0;
406         int seeding_flag = 0;
407         u64 seeding_value = 0;
408         int random_fsid = 0;
409         char *new_fsid_str = NULL;
410         int ret;
411         u64 super_flags = 0;
412
413         optind = 1;
414         while(1) {
415                 int c = getopt(argc, argv, "S:rxfuU:");
416                 if (c < 0)
417                         break;
418                 switch(c) {
419                 case 'S':
420                         seeding_flag = 1;
421                         seeding_value = arg_strtou64(optarg);
422                         break;
423                 case 'r':
424                         super_flags |= BTRFS_FEATURE_INCOMPAT_EXTENDED_IREF;
425                         break;
426                 case 'x':
427                         super_flags |= BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA;
428                         break;
429                 case 'f':
430                         force = 1;
431                         break;
432                 case 'U':
433                         ctree_flags |= OPEN_CTREE_IGNORE_FSID_MISMATCH;
434                         new_fsid_str = optarg;
435                         break;
436                 case 'u':
437                         ctree_flags |= OPEN_CTREE_IGNORE_FSID_MISMATCH;
438                         random_fsid = 1;
439                         break;
440                 default:
441                         print_usage();
442                         return 1;
443                 }
444         }
445
446         set_argv0(argv);
447         argc = argc - optind;
448         device = argv[optind];
449         if (check_argc_exact(argc, 1)) {
450                 print_usage();
451                 return 1;
452         }
453
454         if (random_fsid && new_fsid_str) {
455                 fprintf(stderr,
456                         "ERROR: Random fsid can't be used with specified fsid\n");
457                 return 1;
458         }
459         if (!super_flags && !seeding_flag && !(random_fsid || new_fsid_str)) {
460                 fprintf(stderr,
461                         "ERROR: At least one option should be assigned.\n");
462                 print_usage();
463                 return 1;
464         }
465
466         if (new_fsid_str) {
467                 uuid_t tmp;
468
469                 ret = uuid_parse(new_fsid_str, tmp);
470                 if (ret < 0) {
471                         fprintf(stderr,
472                                 "ERROR: Could not parse UUID: %s\n",
473                                 new_fsid_str);
474                         return 1;
475                 }
476                 if (!test_uuid_unique(new_fsid_str)) {
477                         fprintf(stderr,
478                                 "ERROR: Fsid %s is not unique\n",
479                                 new_fsid_str);
480                         return 1;
481                 }
482         }
483
484         ret = check_mounted(device);
485         if (ret < 0) {
486                 fprintf(stderr, "Could not check mount status: %s\n",
487                         strerror(-ret));
488                 return 1;
489         } else if (ret) {
490                 fprintf(stderr, "%s is mounted\n", device);
491                 return 1;
492         }
493
494         root = open_ctree(device, 0, ctree_flags);
495
496         if (!root) {
497                 fprintf(stderr, "Open ctree failed\n");
498                 return 1;
499         }
500
501         if (seeding_flag) {
502                 if (!seeding_value && !force) {
503                         fprintf(stderr, "Warning: This is dangerous, clearing the seeding flag may cause the derived device not to be mountable!\n");
504                         ret = ask_user("We are going to clear the seeding flag, are you sure?");
505                         if (!ret) {
506                                 fprintf(stderr, "Clear seeding flag canceled\n");
507                                 ret = 1;
508                                 goto out;
509                         }
510                 }
511
512                 ret = update_seeding_flag(root, seeding_value);
513                 if (!ret)
514                         success++;
515                 total++;
516         }
517
518         if (super_flags) {
519                 ret = set_super_incompat_flags(root, super_flags);
520                 if (!ret)
521                         success++;
522                 total++;
523         }
524
525         if (random_fsid || new_fsid_str) {
526                 if (!force) {
527                         fprintf(stderr,
528                                 "Warning: It's highly recommended to run 'btrfs check' before this operation\n");
529                         fprintf(stderr,
530                                 "Also canceling running UUID change progress may cause corruption\n");
531                         ret = ask_user("We are going to change UUID, are your sure?");
532                         if (!ret) {
533                                 fprintf(stderr, "UUID change canceled\n");
534                                 ret = 1;
535                                 goto out;
536                         }
537                 }
538                 ret = change_uuid(root->fs_info, new_fsid_str);
539                 if (!ret)
540                         success++;
541                 total++;
542         }
543
544         if (success == total) {
545                 ret = 0;
546         } else {
547                 root->fs_info->readonly = 1;
548                 ret = 1;
549                 fprintf(stderr, "btrfstune failed\n");
550         }
551 out:
552         close_ctree(root);
553
554         return ret;
555 }