dd5626ef839f71d32e5eda721f2ba65ff0c44678
[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-n \t\tenable no-holes feature (more efficient sparse file representation)\n");
396         fprintf(stderr, "\t-f \t\tforce to do dangerous operation, make sure that you are aware of the dangers\n");
397         fprintf(stderr, "\t-u \t\tchange fsid, use a random one\n");
398         fprintf(stderr, "\t-U UUID\t\tchange fsid to UUID\n");
399 }
400
401 int main(int argc, char *argv[])
402 {
403         struct btrfs_root *root;
404         enum btrfs_open_ctree_flags ctree_flags = OPEN_CTREE_WRITES;
405         int success = 0;
406         int total = 0;
407         int seeding_flag = 0;
408         u64 seeding_value = 0;
409         int random_fsid = 0;
410         char *new_fsid_str = NULL;
411         int ret;
412         u64 super_flags = 0;
413
414         optind = 1;
415         while(1) {
416                 int c = getopt(argc, argv, "S:rxfuU:n");
417                 if (c < 0)
418                         break;
419                 switch(c) {
420                 case 'S':
421                         seeding_flag = 1;
422                         seeding_value = arg_strtou64(optarg);
423                         break;
424                 case 'r':
425                         super_flags |= BTRFS_FEATURE_INCOMPAT_EXTENDED_IREF;
426                         break;
427                 case 'x':
428                         super_flags |= BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA;
429                         break;
430                 case 'n':
431                         super_flags |= BTRFS_FEATURE_INCOMPAT_NO_HOLES;
432                         break;
433                 case 'f':
434                         force = 1;
435                         break;
436                 case 'U':
437                         ctree_flags |= OPEN_CTREE_IGNORE_FSID_MISMATCH;
438                         new_fsid_str = optarg;
439                         break;
440                 case 'u':
441                         ctree_flags |= OPEN_CTREE_IGNORE_FSID_MISMATCH;
442                         random_fsid = 1;
443                         break;
444                 default:
445                         print_usage();
446                         return 1;
447                 }
448         }
449
450         set_argv0(argv);
451         argc = argc - optind;
452         device = argv[optind];
453         if (check_argc_exact(argc, 1)) {
454                 print_usage();
455                 return 1;
456         }
457
458         if (random_fsid && new_fsid_str) {
459                 fprintf(stderr,
460                         "ERROR: Random fsid can't be used with specified fsid\n");
461                 return 1;
462         }
463         if (!super_flags && !seeding_flag && !(random_fsid || new_fsid_str)) {
464                 fprintf(stderr,
465                         "ERROR: At least one option should be assigned.\n");
466                 print_usage();
467                 return 1;
468         }
469
470         if (new_fsid_str) {
471                 uuid_t tmp;
472
473                 ret = uuid_parse(new_fsid_str, tmp);
474                 if (ret < 0) {
475                         fprintf(stderr,
476                                 "ERROR: Could not parse UUID: %s\n",
477                                 new_fsid_str);
478                         return 1;
479                 }
480                 if (!test_uuid_unique(new_fsid_str)) {
481                         fprintf(stderr,
482                                 "ERROR: Fsid %s is not unique\n",
483                                 new_fsid_str);
484                         return 1;
485                 }
486         }
487
488         ret = check_mounted(device);
489         if (ret < 0) {
490                 fprintf(stderr, "Could not check mount status: %s\n",
491                         strerror(-ret));
492                 return 1;
493         } else if (ret) {
494                 fprintf(stderr, "%s is mounted\n", device);
495                 return 1;
496         }
497
498         root = open_ctree(device, 0, ctree_flags);
499
500         if (!root) {
501                 fprintf(stderr, "Open ctree failed\n");
502                 return 1;
503         }
504
505         if (seeding_flag) {
506                 if (!seeding_value && !force) {
507                         fprintf(stderr, "Warning: This is dangerous, clearing the seeding flag may cause the derived device not to be mountable!\n");
508                         ret = ask_user("We are going to clear the seeding flag, are you sure?");
509                         if (!ret) {
510                                 fprintf(stderr, "Clear seeding flag canceled\n");
511                                 ret = 1;
512                                 goto out;
513                         }
514                 }
515
516                 ret = update_seeding_flag(root, seeding_value);
517                 if (!ret)
518                         success++;
519                 total++;
520         }
521
522         if (super_flags) {
523                 ret = set_super_incompat_flags(root, super_flags);
524                 if (!ret)
525                         success++;
526                 total++;
527         }
528
529         if (random_fsid || new_fsid_str) {
530                 if (!force) {
531                         fprintf(stderr,
532                                 "Warning: It's highly recommended to run 'btrfs check' before this operation\n");
533                         fprintf(stderr,
534                                 "Also canceling running UUID change progress may cause corruption\n");
535                         ret = ask_user("We are going to change UUID, are your sure?");
536                         if (!ret) {
537                                 fprintf(stderr, "UUID change canceled\n");
538                                 ret = 1;
539                                 goto out;
540                         }
541                 }
542                 ret = change_uuid(root->fs_info, new_fsid_str);
543                 if (!ret)
544                         success++;
545                 total++;
546         }
547
548         if (success == total) {
549                 ret = 0;
550         } else {
551                 root->fs_info->readonly = 1;
552                 ret = 1;
553                 fprintf(stderr, "btrfstune failed\n");
554         }
555 out:
556         close_ctree(root);
557
558         return ret;
559 }