diff options
author | Dimitri John Ledkov <xnox@ubuntu.com> | 2019-01-26 00:37:47 +0000 |
---|---|---|
committer | Dimitri John Ledkov <xnox@ubuntu.com> | 2019-01-26 00:37:47 +0000 |
commit | 2fd02b1f5f50d499b8f19d0cc5e9bc9ed7e686cd (patch) | |
tree | 7557002a8de5892b17c04d1afcd8fdc99a0af4e7 /btrfstune.c | |
parent | 03b9f8baf40383f4c2d709c656ca35bd75362dff (diff) |
New upstream release
Diffstat (limited to 'btrfstune.c')
-rw-r--r-- | btrfstune.c | 251 |
1 files changed, 196 insertions, 55 deletions
diff --git a/btrfstune.c b/btrfstune.c index 1e378ba1..7e6d0ac2 100644 --- a/btrfstune.c +++ b/btrfstune.c @@ -73,6 +73,116 @@ static int update_seeding_flag(struct btrfs_root *root, int set_flag) return ret; } +/* + * Return 0 for no unfinished fsid change. + * Return >0 for unfinished fsid change, and restore unfinished fsid/ + * chunk_tree_id into fsid_ret/chunk_id_ret. + */ +static int check_unfinished_fsid_change(struct btrfs_fs_info *fs_info, + uuid_t fsid_ret, uuid_t chunk_id_ret) +{ + struct btrfs_root *tree_root = fs_info->tree_root; + u64 flags = btrfs_super_flags(fs_info->super_copy); + + if (flags & (BTRFS_SUPER_FLAG_CHANGING_FSID | + BTRFS_SUPER_FLAG_CHANGING_FSID_V2)) { + memcpy(fsid_ret, fs_info->super_copy->fsid, BTRFS_FSID_SIZE); + read_extent_buffer(tree_root->node, chunk_id_ret, + btrfs_header_chunk_tree_uuid(tree_root->node), + BTRFS_UUID_SIZE); + return 1; + } + return 0; +} + +static int set_metadata_uuid(struct btrfs_root *root, const char *uuid_string) +{ + struct btrfs_super_block *disk_super; + uuid_t new_fsid, unused1, unused2; + struct btrfs_trans_handle *trans; + bool new_uuid = true; + u64 incompat_flags; + bool uuid_changed; + u64 super_flags; + int ret; + + disk_super = root->fs_info->super_copy; + super_flags = btrfs_super_flags(disk_super); + incompat_flags = btrfs_super_incompat_flags(disk_super); + uuid_changed = incompat_flags & BTRFS_FEATURE_INCOMPAT_METADATA_UUID; + + if (super_flags & BTRFS_SUPER_FLAG_SEEDING) { + fprintf(stderr, "cannot set metadata UUID on a seed device\n"); + return 1; + } + + if (check_unfinished_fsid_change(root->fs_info, unused1, unused2)) { + fprintf(stderr, + "UUID rewrite in progress, cannot change fsid\n"); + return 1; + } + + if (uuid_string) + uuid_parse(uuid_string, new_fsid); + else + uuid_generate(new_fsid); + + new_uuid = (memcmp(new_fsid, disk_super->fsid, BTRFS_FSID_SIZE) != 0); + + /* Step 1 sets the in progress flag */ + trans = btrfs_start_transaction(root, 1); + super_flags |= BTRFS_SUPER_FLAG_CHANGING_FSID_V2; + btrfs_set_super_flags(disk_super, super_flags); + ret = btrfs_commit_transaction(trans, root); + if (ret < 0) + return ret; + + if (new_uuid && uuid_changed && memcmp(disk_super->metadata_uuid, + new_fsid, BTRFS_FSID_SIZE) == 0) { + /* + * Changing fsid to be the same as metadata uuid, so just + * disable the flag + */ + memcpy(disk_super->fsid, &new_fsid, BTRFS_FSID_SIZE); + incompat_flags &= ~BTRFS_FEATURE_INCOMPAT_METADATA_UUID; + btrfs_set_super_incompat_flags(disk_super, incompat_flags); + memset(disk_super->metadata_uuid, 0, BTRFS_FSID_SIZE); + } else if (new_uuid && uuid_changed && memcmp(disk_super->metadata_uuid, + new_fsid, BTRFS_FSID_SIZE)) { + /* + * Changing fsid on an already changed FS, in this case we + * only change the fsid and don't touch metadata uuid as it + * has already the correct value + */ + memcpy(disk_super->fsid, &new_fsid, BTRFS_FSID_SIZE); + } else if (new_uuid && !uuid_changed) { + /* + * First time changing the fsid, copy the fsid to metadata_uuid + */ + incompat_flags |= BTRFS_FEATURE_INCOMPAT_METADATA_UUID; + btrfs_set_super_incompat_flags(disk_super, incompat_flags); + memcpy(disk_super->metadata_uuid, disk_super->fsid, + BTRFS_FSID_SIZE); + memcpy(disk_super->fsid, &new_fsid, BTRFS_FSID_SIZE); + } else { + /* Setting the same fsid as current, do nothing */ + return 0; + } + + trans = btrfs_start_transaction(root, 1); + + /* + * Step 2 is to write the metadata_uuid, set the incompat flag and + * clear the in progress flag + */ + super_flags &= ~BTRFS_SUPER_FLAG_CHANGING_FSID_V2; + btrfs_set_super_flags(disk_super, super_flags); + + /* Then actually copy the metadata uuid and set the incompat bit */ + + return btrfs_commit_transaction(trans, root); +} + static int set_super_incompat_flags(struct btrfs_root *root, u64 flags) { struct btrfs_trans_handle *trans; @@ -91,15 +201,15 @@ static int set_super_incompat_flags(struct btrfs_root *root, u64 flags) return ret; } -static int change_buffer_header_uuid(struct extent_buffer *eb) +static int change_buffer_header_uuid(struct extent_buffer *eb, uuid_t new_fsid) { struct btrfs_fs_info *fs_info = eb->fs_info; int same_fsid = 1; int same_chunk_tree_uuid = 1; int ret; - same_fsid = !memcmp_extent_buffer(eb, fs_info->new_fsid, - btrfs_header_fsid(), BTRFS_FSID_SIZE); + same_fsid = !memcmp_extent_buffer(eb, new_fsid, btrfs_header_fsid(), + BTRFS_FSID_SIZE); same_chunk_tree_uuid = !memcmp_extent_buffer(eb, fs_info->new_chunk_tree_uuid, btrfs_header_chunk_tree_uuid(eb), @@ -107,7 +217,7 @@ static int change_buffer_header_uuid(struct extent_buffer *eb) if (same_fsid && same_chunk_tree_uuid) return 0; if (!same_fsid) - write_extent_buffer(eb, fs_info->new_fsid, btrfs_header_fsid(), + write_extent_buffer(eb, new_fsid, btrfs_header_fsid(), BTRFS_FSID_SIZE); if (!same_chunk_tree_uuid) write_extent_buffer(eb, fs_info->new_chunk_tree_uuid, @@ -118,7 +228,7 @@ static int change_buffer_header_uuid(struct extent_buffer *eb) return ret; } -static int change_extents_uuid(struct btrfs_fs_info *fs_info) +static int change_extents_uuid(struct btrfs_fs_info *fs_info, uuid_t new_fsid) { struct btrfs_root *root = fs_info->extent_root; struct btrfs_path path; @@ -157,7 +267,7 @@ static int change_extents_uuid(struct btrfs_fs_info *fs_info) ret = PTR_ERR(eb); goto out; } - ret = change_buffer_header_uuid(eb); + ret = change_buffer_header_uuid(eb, new_fsid); free_extent_buffer(eb); if (ret < 0) { error("failed to change uuid of tree block: %llu", @@ -179,29 +289,28 @@ out: return ret; } -static int change_device_uuid(struct extent_buffer *eb, int slot) +static int change_device_uuid(struct extent_buffer *eb, int slot, + uuid_t new_fsid) { struct btrfs_dev_item *di; struct btrfs_fs_info *fs_info = eb->fs_info; int ret = 0; di = btrfs_item_ptr(eb, slot, struct btrfs_dev_item); - if (!memcmp_extent_buffer(eb, fs_info->new_fsid, + if (!memcmp_extent_buffer(eb, new_fsid, (unsigned long)btrfs_device_fsid(di), BTRFS_FSID_SIZE)) return ret; - write_extent_buffer(eb, fs_info->new_fsid, - (unsigned long)btrfs_device_fsid(di), + write_extent_buffer(eb, new_fsid, (unsigned long)btrfs_device_fsid(di), BTRFS_FSID_SIZE); ret = write_tree_block(NULL, fs_info, eb); return ret; } -static int change_devices_uuid(struct btrfs_fs_info *fs_info) +static int change_devices_uuid(struct btrfs_root *root, uuid_t new_fsid) { - struct btrfs_root *root = fs_info->chunk_root; struct btrfs_path path; struct btrfs_key key = {0, 0, 0}; int ret = 0; @@ -217,7 +326,8 @@ static int change_devices_uuid(struct btrfs_fs_info *fs_info) if (key.type != BTRFS_DEV_ITEM_KEY || key.objectid != BTRFS_DEV_ITEMS_OBJECTID) goto next; - ret = change_device_uuid(path.nodes[0], path.slots[0]); + ret = change_device_uuid(path.nodes[0], path.slots[0], + new_fsid); if (ret < 0) goto out; next: @@ -234,7 +344,7 @@ out: return ret; } -static int change_fsid_prepare(struct btrfs_fs_info *fs_info) +static int change_fsid_prepare(struct btrfs_fs_info *fs_info, uuid_t new_fsid) { struct btrfs_root *tree_root = fs_info->tree_root; u64 flags = btrfs_super_flags(fs_info->super_copy); @@ -243,11 +353,14 @@ static int change_fsid_prepare(struct btrfs_fs_info *fs_info) flags |= BTRFS_SUPER_FLAG_CHANGING_FSID; btrfs_set_super_flags(fs_info->super_copy, flags); - memcpy(fs_info->super_copy->fsid, fs_info->new_fsid, BTRFS_FSID_SIZE); + memcpy(fs_info->super_copy->fsid, new_fsid, BTRFS_FSID_SIZE); ret = write_all_supers(fs_info); if (ret < 0) return ret; + /* Also need to change the metadatauuid of the fs info */ + memcpy(fs_info->fs_devices->metadata_uuid, new_fsid, BTRFS_FSID_SIZE); + /* also restore new chunk_tree_id into tree_root for restore */ write_extent_buffer(tree_root->node, fs_info->new_chunk_tree_uuid, btrfs_header_chunk_tree_uuid(tree_root->node), @@ -265,26 +378,6 @@ static int change_fsid_done(struct btrfs_fs_info *fs_info) return write_all_supers(fs_info); } -/* - * Return 0 for no unfinished fsid change. - * Return >0 for unfinished fsid change, and restore unfinished fsid/ - * chunk_tree_id into fsid_ret/chunk_id_ret. - */ -static int check_unfinished_fsid_change(struct btrfs_fs_info *fs_info, - uuid_t fsid_ret, uuid_t chunk_id_ret) -{ - struct btrfs_root *tree_root = fs_info->tree_root; - u64 flags = btrfs_super_flags(fs_info->super_copy); - - if (flags & BTRFS_SUPER_FLAG_CHANGING_FSID) { - memcpy(fsid_ret, fs_info->super_copy->fsid, BTRFS_FSID_SIZE); - read_extent_buffer(tree_root->node, chunk_id_ret, - btrfs_header_chunk_tree_uuid(tree_root->node), - BTRFS_UUID_SIZE); - return 1; - } - return 0; -} /* * Change fsid of a given fs. @@ -320,10 +413,9 @@ static int change_uuid(struct btrfs_fs_info *fs_info, const char *new_fsid_str) uuid_generate(new_chunk_id); } - fs_info->new_fsid = new_fsid; fs_info->new_chunk_tree_uuid = new_chunk_id; - memcpy(old_fsid, (const char*)fs_info->fsid, BTRFS_UUID_SIZE); + memcpy(old_fsid, (const char*)fs_info->fs_devices->fsid, BTRFS_UUID_SIZE); uuid_unparse(old_fsid, uuid_buf); printf("Current fsid: %s\n", uuid_buf); @@ -331,13 +423,13 @@ static int change_uuid(struct btrfs_fs_info *fs_info, const char *new_fsid_str) printf("New fsid: %s\n", uuid_buf); /* Now we can begin fsid change */ printf("Set superblock flag CHANGING_FSID\n"); - ret = change_fsid_prepare(fs_info); + ret = change_fsid_prepare(fs_info, new_fsid); if (ret < 0) goto out; /* Change extents first */ printf("Change fsid in extents\n"); - ret = change_extents_uuid(fs_info); + ret = change_extents_uuid(fs_info, new_fsid); if (ret < 0) { error("failed to change UUID of metadata: %d", ret); goto out; @@ -345,17 +437,15 @@ static int change_uuid(struct btrfs_fs_info *fs_info, const char *new_fsid_str) /* Then devices */ printf("Change fsid on devices\n"); - ret = change_devices_uuid(fs_info); + ret = change_devices_uuid(fs_info->chunk_root, new_fsid); if (ret < 0) { error("failed to change UUID of devices: %d", ret); goto out; } /* Last, change fsid in super */ - memcpy(fs_info->fs_devices->fsid, fs_info->new_fsid, - BTRFS_FSID_SIZE); - memcpy(fs_info->super_copy->fsid, fs_info->new_fsid, - BTRFS_FSID_SIZE); + memcpy(fs_info->fs_devices->fsid, new_fsid, BTRFS_FSID_SIZE); + memcpy(fs_info->super_copy->fsid, new_fsid, BTRFS_FSID_SIZE); ret = write_all_supers(fs_info); if (ret < 0) goto out; @@ -363,7 +453,6 @@ static int change_uuid(struct btrfs_fs_info *fs_info, const char *new_fsid_str) /* Now fsid change is done */ printf("Clear superblock flag CHANGING_FSID\n"); ret = change_fsid_done(fs_info); - fs_info->new_fsid = NULL; fs_info->new_chunk_tree_uuid = NULL; printf("Fsid change finished\n"); out: @@ -373,13 +462,22 @@ out: static void print_usage(void) { printf("usage: btrfstune [options] device\n"); - printf("\t-S value\tpositive value will enable seeding, zero to disable, negative is not allowed\n"); - printf("\t-r \t\tenable extended inode refs\n"); - printf("\t-x \t\tenable skinny metadata extent refs\n"); - printf("\t-n \t\tenable no-holes feature (more efficient sparse file representation)\n"); - printf("\t-f \t\tforce to do dangerous operation, make sure that you are aware of the dangers\n"); - printf("\t-u \t\tchange fsid, use a random one\n"); - printf("\t-U UUID\t\tchange fsid to UUID\n"); + printf("Tune settings of filesystem features on an unmounted device\n\n"); + printf("Options:\n"); + printf(" change feature status:\n"); + printf("\t-r enable extended inode refs (mkfs: extref, for hardlink limits)\n"); + printf("\t-x enable skinny metadata extent refs (mkfs: skinny-metadata)\n"); + printf("\t-n enable no-holes feature (mkfs: no-holes, more efficient sparse file representation)\n"); + printf("\t-S <0|1> set/unset seeding status of a device\n"); + printf(" uuid changes:\n"); + printf("\t-u rewrite fsid, use a random one\n"); + printf("\t-U UUID rewrite fsid to UUID\n"); + printf("\t-m change fsid in metadata_uuid to a random UUID\n"); + printf("\t (incompat change, more lightweight than -u|-U)\n"); + printf("\t-M UUID change fsid in metadata_uuid to UUID\n"); + printf(" general:\n"); + printf("\t-f allow dangerous operations, make sure that you are aware of the dangers\n"); + printf("\t--help print this help\n"); } int main(int argc, char *argv[]) @@ -391,6 +489,7 @@ int main(int argc, char *argv[]) int seeding_flag = 0; u64 seeding_value = 0; int random_fsid = 0; + int change_metadata_uuid = 0; char *new_fsid_str = NULL; int ret; u64 super_flags = 0; @@ -401,7 +500,7 @@ int main(int argc, char *argv[]) { "help", no_argument, NULL, GETOPT_VAL_HELP}, { NULL, 0, NULL, 0 } }; - int c = getopt_long(argc, argv, "S:rxfuU:n", long_options, NULL); + int c = getopt_long(argc, argv, "S:rxfuU:nmM:", long_options, NULL); if (c < 0) break; @@ -430,6 +529,15 @@ int main(int argc, char *argv[]) ctree_flags |= OPEN_CTREE_IGNORE_FSID_MISMATCH; random_fsid = 1; break; + case 'M': + ctree_flags |= OPEN_CTREE_IGNORE_FSID_MISMATCH; + change_metadata_uuid = 1; + new_fsid_str = optarg; + break; + case 'm': + ctree_flags |= OPEN_CTREE_IGNORE_FSID_MISMATCH; + change_metadata_uuid = 1; + break; case GETOPT_VAL_HELP: default: print_usage(); @@ -448,7 +556,8 @@ int main(int argc, char *argv[]) error("random fsid can't be used with specified fsid"); return 1; } - if (!super_flags && !seeding_flag && !(random_fsid || new_fsid_str)) { + if (!super_flags && !seeding_flag && !(random_fsid || new_fsid_str) && + !change_metadata_uuid) { error("at least one option should be specified"); print_usage(); return 1; @@ -495,6 +604,12 @@ int main(int argc, char *argv[]) } if (seeding_flag) { + if (btrfs_fs_incompat(root->fs_info, METADATA_UUID)) { + fprintf(stderr, "SEED flag cannot be changed on a metadata-uuid changed fs\n"); + ret = 1; + goto out; + } + if (!seeding_value && !force) { warning( "this is dangerous, clearing the seeding flag may cause the derived device not to be mountable!"); @@ -519,7 +634,33 @@ int main(int argc, char *argv[]) total++; } - if (random_fsid || new_fsid_str) { + if (change_metadata_uuid) { + if (seeding_flag) { + fprintf(stderr, + "Not allowed to set both seeding flag and uuid metadata\n"); + ret = 1; + goto out; + } + + if (new_fsid_str) + ret = set_metadata_uuid(root, new_fsid_str); + else + ret = set_metadata_uuid(root, NULL); + + if (!ret) + success++; + total++; + } + + if (random_fsid || (new_fsid_str && !change_metadata_uuid)) { + if (btrfs_fs_incompat(root->fs_info, METADATA_UUID)) { + fprintf(stderr, + "Cannot rewrite fsid while METADATA_UUID flag is active. \n" + "Ensure fsid and metadata_uuid match before retrying.\n"); + ret = 1; + goto out; + } + if (!force) { warning( "it's recommended to run 'btrfs check --readonly' before this operation.\n" |