X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=mkfs%2Fmain.c;h=9bfddf3018dd3729ef899f24ff7231d6bd125cf1;hb=e181c03d420ef36c8bedd86492dc5c7b229be8a9;hp=2a415ae931a6e03c605de8712ba7a3f817f8d712;hpb=075580471e0f7435c52df486eb4354031337a7d3;p=platform%2Fupstream%2Fbtrfs-progs.git diff --git a/mkfs/main.c b/mkfs/main.c index 2a415ae..9bfddf3 100644 --- a/mkfs/main.c +++ b/mkfs/main.c @@ -26,12 +26,12 @@ #include /* #include included via androidcompat.h */ #include +#include #include #include #include #include #include -#include #include "ctree.h" #include "disk-io.h" #include "volumes.h" @@ -67,10 +67,14 @@ static int create_metadata_block_groups(struct btrfs_root *root, int mixed, bytes_used = btrfs_super_bytes_used(fs_info->super_copy); root->fs_info->system_allocs = 1; + /* + * First temporary system chunk must match the chunk layout + * created in make_btrfs(). + */ ret = btrfs_make_block_group(trans, fs_info, bytes_used, BTRFS_BLOCK_GROUP_SYSTEM, - BTRFS_FIRST_CHUNK_TREE_OBJECTID, - 0, BTRFS_MKFS_SYSTEM_GROUP_SIZE); + BTRFS_BLOCK_RESERVED_1M_FOR_SUPER, + BTRFS_MKFS_SYSTEM_GROUP_SIZE); allocation->system += BTRFS_MKFS_SYSTEM_GROUP_SIZE; if (ret) return ret; @@ -89,7 +93,6 @@ static int create_metadata_block_groups(struct btrfs_root *root, int mixed, ret = btrfs_make_block_group(trans, fs_info, 0, BTRFS_BLOCK_GROUP_METADATA | BTRFS_BLOCK_GROUP_DATA, - BTRFS_FIRST_CHUNK_TREE_OBJECTID, chunk_start, chunk_size); if (ret) return ret; @@ -106,7 +109,6 @@ static int create_metadata_block_groups(struct btrfs_root *root, int mixed, return ret; ret = btrfs_make_block_group(trans, fs_info, 0, BTRFS_BLOCK_GROUP_METADATA, - BTRFS_FIRST_CHUNK_TREE_OBJECTID, chunk_start, chunk_size); allocation->metadata += chunk_size; if (ret) @@ -141,7 +143,6 @@ static int create_data_block_groups(struct btrfs_trans_handle *trans, return ret; ret = btrfs_make_block_group(trans, fs_info, 0, BTRFS_BLOCK_GROUP_DATA, - BTRFS_FIRST_CHUNK_TREE_OBJECTID, chunk_start, chunk_size); allocation->data += chunk_size; if (ret) @@ -250,8 +251,7 @@ static int create_one_raid_group(struct btrfs_trans_handle *trans, return ret; ret = btrfs_make_block_group(trans, fs_info, 0, - type, BTRFS_FIRST_CHUNK_TREE_OBJECTID, - chunk_start, chunk_size); + type, chunk_start, chunk_size); type &= BTRFS_BLOCK_GROUP_TYPE_MASK; if (type == BTRFS_BLOCK_GROUP_DATA) { @@ -315,6 +315,7 @@ static int create_tree(struct btrfs_trans_handle *trans, struct btrfs_key location; struct btrfs_root_item root_item; struct extent_buffer *tmp; + u8 uuid[BTRFS_UUID_SIZE] = {0}; int ret; ret = btrfs_copy_root(trans, root, root->node, &tmp, objectid); @@ -325,6 +326,10 @@ static int create_tree(struct btrfs_trans_handle *trans, btrfs_set_root_bytenr(&root_item, tmp->start); btrfs_set_root_level(&root_item, btrfs_header_level(tmp)); btrfs_set_root_generation(&root_item, trans->transid); + /* clear uuid and o/ctime of source tree */ + memcpy(root_item.uuid, uuid, BTRFS_UUID_SIZE); + btrfs_set_stack_timespec_sec(&root_item.otime, 0); + btrfs_set_stack_timespec_sec(&root_item.ctime, 0); free_extent_buffer(tmp); location.objectid = objectid; @@ -353,6 +358,7 @@ static void print_usage(int ret) printf(" creation:\n"); printf("\t-b|--byte-count SIZE set filesystem size to SIZE (on the first device)\n"); printf("\t-r|--rootdir DIR copy files from DIR to the image root directory\n"); + printf("\t--shrink (with --rootdir) shrink the filled filesystem to minimal size\n"); printf("\t-K|--nodiscard do not perform whole device TRIM\n"); printf("\t-f|--force force overwrite of existing filesystem\n"); printf(" general:\n"); @@ -401,114 +407,6 @@ static char *parse_label(const char *input) return strdup(input); } -static int create_chunks(struct btrfs_trans_handle *trans, - struct btrfs_root *root, u64 num_of_meta_chunks, - u64 size_of_data, - struct mkfs_allocation *allocation) -{ - struct btrfs_fs_info *fs_info = root->fs_info; - u64 chunk_start; - u64 chunk_size; - u64 meta_type = BTRFS_BLOCK_GROUP_METADATA; - u64 data_type = BTRFS_BLOCK_GROUP_DATA; - u64 minimum_data_chunk_size = SZ_8M; - u64 i; - int ret; - - for (i = 0; i < num_of_meta_chunks; i++) { - ret = btrfs_alloc_chunk(trans, fs_info, - &chunk_start, &chunk_size, meta_type); - if (ret) - return ret; - ret = btrfs_make_block_group(trans, fs_info, 0, - meta_type, BTRFS_FIRST_CHUNK_TREE_OBJECTID, - chunk_start, chunk_size); - allocation->metadata += chunk_size; - if (ret) - return ret; - set_extent_dirty(&root->fs_info->free_space_cache, - chunk_start, chunk_start + chunk_size - 1); - } - - if (size_of_data < minimum_data_chunk_size) - size_of_data = minimum_data_chunk_size; - - ret = btrfs_alloc_data_chunk(trans, fs_info, - &chunk_start, size_of_data, data_type, 0); - if (ret) - return ret; - ret = btrfs_make_block_group(trans, fs_info, 0, - data_type, BTRFS_FIRST_CHUNK_TREE_OBJECTID, - chunk_start, size_of_data); - allocation->data += size_of_data; - if (ret) - return ret; - set_extent_dirty(&root->fs_info->free_space_cache, - chunk_start, chunk_start + size_of_data - 1); - return ret; -} - -/* - * This ignores symlinks with unreadable targets and subdirs that can't - * be read. It's a best-effort to give a rough estimate of the size of - * a subdir. It doesn't guarantee that prepopulating btrfs from this - * tree won't still run out of space. - */ -static u64 global_total_size; -static u64 fs_block_size; -static int ftw_add_entry_size(const char *fpath, const struct stat *st, - int type) -{ - if (type == FTW_F || type == FTW_D) - global_total_size += round_up(st->st_size, fs_block_size); - - return 0; -} - -static u64 size_sourcedir(const char *dir_name, u64 sectorsize, - u64 *num_of_meta_chunks_ret, u64 *size_of_data_ret) -{ - u64 dir_size = 0; - u64 total_size = 0; - int ret; - u64 default_chunk_size = SZ_8M; - u64 allocated_meta_size = SZ_8M; - u64 allocated_total_size = 20 * SZ_1M; /* 20MB */ - u64 num_of_meta_chunks = 0; - u64 num_of_data_chunks = 0; - u64 num_of_allocated_meta_chunks = - allocated_meta_size / default_chunk_size; - - global_total_size = 0; - fs_block_size = sectorsize; - ret = ftw(dir_name, ftw_add_entry_size, 10); - dir_size = global_total_size; - if (ret < 0) { - error("ftw subdir walk of %s failed: %s", dir_name, - strerror(errno)); - exit(1); - } - - num_of_data_chunks = (dir_size + default_chunk_size - 1) / - default_chunk_size; - - num_of_meta_chunks = (dir_size / 2) / default_chunk_size; - if (((dir_size / 2) % default_chunk_size) != 0) - num_of_meta_chunks++; - if (num_of_meta_chunks <= num_of_allocated_meta_chunks) - num_of_meta_chunks = 0; - else - num_of_meta_chunks -= num_of_allocated_meta_chunks; - - total_size = allocated_total_size + - (num_of_data_chunks * default_chunk_size) + - (num_of_meta_chunks * default_chunk_size); - - *num_of_meta_chunks_ret = num_of_meta_chunks; - *size_of_data_ret = num_of_data_chunks * default_chunk_size; - return total_size; -} - static int zero_output_file(int out_fd, u64 size) { int loop_num; @@ -779,6 +677,38 @@ out: return ret; } +/* + * Just update chunk allocation info, since --rootdir may allocate new + * chunks which is not updated in @allocation structure. + */ +static void update_chunk_allocation(struct btrfs_fs_info *fs_info, + struct mkfs_allocation *allocation) +{ + struct btrfs_block_group_cache *bg_cache; + const u64 mixed_flag = BTRFS_BLOCK_GROUP_DATA | BTRFS_BLOCK_GROUP_METADATA; + u64 search_start = 0; + + allocation->mixed = 0; + allocation->data = 0; + allocation->metadata = 0; + allocation->system = 0; + while (1) { + bg_cache = btrfs_lookup_first_block_group(fs_info, + search_start); + if (!bg_cache) + break; + if ((bg_cache->flags & mixed_flag) == mixed_flag) + allocation->mixed += bg_cache->key.offset; + else if (bg_cache->flags & BTRFS_BLOCK_GROUP_DATA) + allocation->data += bg_cache->key.offset; + else if (bg_cache->flags & BTRFS_BLOCK_GROUP_METADATA) + allocation->metadata += bg_cache->key.offset; + else + allocation->system += bg_cache->key.offset; + search_start = bg_cache->key.objectid + bg_cache->key.offset; + } +} + int main(int argc, char **argv) { char *file; @@ -797,7 +727,7 @@ int main(int argc, char **argv) u32 stripesize = 4096; int zero_end = 1; int fd = -1; - int ret; + int ret = 0; int close_ret; int i; int mixed = 0; @@ -808,11 +738,11 @@ int main(int argc, char **argv) int ssd = 0; int force_overwrite = 0; char *source_dir = NULL; - int source_dir_set = 0; - u64 num_of_meta_chunks = 0; - u64 size_of_data = 0; + bool source_dir_set = false; + bool shrink_rootdir = false; u64 source_dir_size = 0; u64 min_dev_size; + u64 shrink_size; int dev_cnt = 0; int saved_optind; char fs_uuid[BTRFS_UUID_UNPARSED_SIZE] = { 0 }; @@ -822,6 +752,7 @@ int main(int argc, char **argv) while(1) { int c; + enum { GETOPT_VAL_SHRINK = 257 }; static const struct option long_options[] = { { "alloc-start", required_argument, NULL, 'A'}, { "byte-count", required_argument, NULL, 'b' }, @@ -839,6 +770,7 @@ int main(int argc, char **argv) { "features", required_argument, NULL, 'O' }, { "uuid", required_argument, NULL, 'U' }, { "quiet", 0, NULL, 'q' }, + { "shrink", no_argument, NULL, GETOPT_VAL_SHRINK }, { "help", no_argument, NULL, GETOPT_VAL_HELP }, { NULL, 0, NULL, 0} }; @@ -906,7 +838,7 @@ int main(int argc, char **argv) goto success; case 'r': source_dir = optarg; - source_dir_set = 1; + source_dir_set = true; break; case 'U': strncpy(fs_uuid, optarg, @@ -918,6 +850,9 @@ int main(int argc, char **argv) case 'q': verbose = 0; break; + case GETOPT_VAL_SHRINK: + shrink_rootdir = true; + break; case GETOPT_VAL_HELP: default: print_usage(c != GETOPT_VAL_HELP); @@ -940,6 +875,10 @@ int main(int argc, char **argv) error("the option -r is limited to a single device"); goto error; } + if (shrink_rootdir && !source_dir_set) { + error("the option --shrink must be used with --rootdir"); + goto error; + } if (*fs_uuid) { uuid_t dummy_uuid; @@ -956,7 +895,9 @@ int main(int argc, char **argv) while (dev_cnt-- > 0) { file = argv[optind++]; - if (is_block_device(file) == 1) + if (source_dir_set && is_path_exist(file) == 0) + ret = 0; + else if (is_block_device(file) == 1) ret = test_dev_for_mkfs(file, force_overwrite); else ret = test_status_for_mkfs(file, force_overwrite); @@ -1029,6 +970,53 @@ int main(int argc, char **argv) min_dev_size = btrfs_min_dev_size(nodesize, mixed, metadata_profile, data_profile); + /* + * Enlarge the destination file or create a new one, using the size + * calculated from source dir. + * + * This must be done before minimal device size checks. + */ + if (source_dir_set) { + int oflags = O_RDWR; + struct stat statbuf; + + if (is_path_exist(file) == 0) + oflags |= O_CREAT; + + fd = open(file, oflags, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | + S_IROTH); + if (fd < 0) { + error("unable to open %s: %m", file); + goto error; + } + + ret = fstat(fd, &statbuf); + if (ret < 0) { + error("unable to stat %s: %m", file); + ret = -errno; + goto error; + } + + /* + * Block_count not specified, use file/device size first. + * Or we will always use source_dir_size calculated for mkfs. + */ + if (!block_count) + block_count = btrfs_device_size(fd, &statbuf); + source_dir_size = btrfs_mkfs_size_dir(source_dir, sectorsize, + min_dev_size, metadata_profile, data_profile); + if (block_count < source_dir_size) + block_count = source_dir_size; + ret = zero_output_file(fd, block_count); + if (ret) { + error("unable to zero the output file"); + close(fd); + goto error; + } + /* our "device" is the new image file */ + dev_block_count = block_count; + close(fd); + } /* Check device/block_count after the nodesize is determined */ if (block_count && block_count < min_dev_size) { error("size %llu is too small to make a usable filesystem", @@ -1043,8 +1031,7 @@ int main(int argc, char **argv) path = argv[i]; ret = test_minimum_size(path, min_dev_size); if (ret < 0) { - error("failed to check size for %s: %s", - path, strerror(-ret)); + error("failed to check size for %s: %m", path); goto error; } if (ret > 0) { @@ -1062,51 +1049,27 @@ int main(int argc, char **argv) dev_cnt--; - if (!source_dir_set) { - /* - * open without O_EXCL so that the problem should not - * occur by the following processing. - * (btrfs_register_one_device() fails if O_EXCL is on) - */ - fd = open(file, O_RDWR); - if (fd < 0) { - error("unable to open %s: %s", file, strerror(errno)); - goto error; - } - ret = btrfs_prepare_device(fd, file, &dev_block_count, - block_count, - (zero_end ? PREP_DEVICE_ZERO_END : 0) | - (discard ? PREP_DEVICE_DISCARD : 0) | - (verbose ? PREP_DEVICE_VERBOSE : 0)); - if (ret) { - goto error; - } - if (block_count && block_count > dev_block_count) { - error("%s is smaller than requested size, expected %llu, found %llu", - file, - (unsigned long long)block_count, - (unsigned long long)dev_block_count); - goto error; - } - } else { - fd = open(file, O_CREAT | O_RDWR, - S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); - if (fd < 0) { - error("unable to open %s: %s", file, strerror(errno)); - goto error; - } - - source_dir_size = size_sourcedir(source_dir, sectorsize, - &num_of_meta_chunks, &size_of_data); - if(block_count < source_dir_size) - block_count = source_dir_size; - ret = zero_output_file(fd, block_count); - if (ret) { - error("unable to zero the output file"); - goto error; - } - /* our "device" is the new image file */ - dev_block_count = block_count; + /* + * Open without O_EXCL so that the problem should not occur by the + * following operation in kernel: + * (btrfs_register_one_device() fails if O_EXCL is on) + */ + fd = open(file, O_RDWR); + if (fd < 0) { + error("unable to open %s: %m", file); + goto error; + } + ret = btrfs_prepare_device(fd, file, &dev_block_count, block_count, + (zero_end ? PREP_DEVICE_ZERO_END : 0) | + (discard ? PREP_DEVICE_DISCARD : 0) | + (verbose ? PREP_DEVICE_VERBOSE : 0)); + if (ret) + goto error; + if (block_count && block_count > dev_block_count) { + error("%s is smaller than requested size, expected %llu, found %llu", + file, (unsigned long long)block_count, + (unsigned long long)dev_block_count); + goto error; } /* To create the first block group and chunk 0 in make_btrfs */ @@ -1136,7 +1099,7 @@ int main(int argc, char **argv) } fs_info = open_ctree_fs_info(file, 0, 0, 0, - OPEN_CTREE_WRITES | OPEN_CTREE_FS_PARTIAL); + OPEN_CTREE_WRITES | OPEN_CTREE_TEMPORARY_SUPER); if (!fs_info) { error("open ctree failed"); goto error; @@ -1195,7 +1158,7 @@ int main(int argc, char **argv) */ fd = open(file, O_RDWR); if (fd < 0) { - error("unable to open %s: %s", file, strerror(errno)); + error("unable to open %s: %m", file); goto error; } ret = btrfs_device_already_in_root(root, fd, @@ -1232,13 +1195,11 @@ int main(int argc, char **argv) } raid_groups: - if (!source_dir_set) { - ret = create_raid_groups(trans, root, data_profile, - metadata_profile, mixed, &allocation); - if (ret) { - error("unable to create raid groups: %d", ret); - goto out; - } + ret = create_raid_groups(trans, root, data_profile, + metadata_profile, mixed, &allocation); + if (ret) { + error("unable to create raid groups: %d", ret); + goto out; } ret = create_tree(trans, root, BTRFS_DATA_RELOC_TREE_OBJECTID); @@ -1247,44 +1208,45 @@ raid_groups: goto out; } + ret = create_tree(trans, root, BTRFS_UUID_TREE_OBJECTID); + if (ret) + warning( + "unable to create uuid tree, will be created after mount: %d", ret); + ret = btrfs_commit_transaction(trans, root); if (ret) { error("unable to commit transaction: %d", ret); goto out; } - if (source_dir_set) { - trans = btrfs_start_transaction(root, 1); - BUG_ON(IS_ERR(trans)); - ret = create_chunks(trans, root, - num_of_meta_chunks, size_of_data, - &allocation); - if (ret) { - error("unable to create chunks: %d", ret); - goto out; - } - ret = btrfs_commit_transaction(trans, root); - if (ret) { - error("transaction commit failed: %d", ret); - goto out; - } + ret = cleanup_temp_chunks(fs_info, &allocation, data_profile, + metadata_profile, metadata_profile); + if (ret < 0) { + error("failed to cleanup temporary chunks: %d", ret); + goto out; + } + if (source_dir_set) { ret = btrfs_mkfs_fill_dir(source_dir, root, verbose); if (ret) { error("error wihle filling filesystem: %d", ret); goto out; } - } - ret = cleanup_temp_chunks(fs_info, &allocation, data_profile, - metadata_profile, metadata_profile); - if (ret < 0) { - error("failed to cleanup temporary chunks: %d", ret); - goto out; + if (shrink_rootdir) { + ret = btrfs_mkfs_shrink_fs(fs_info, &shrink_size, + shrink_rootdir); + if (ret < 0) { + error("error while shrinking filesystem: %d", + ret); + goto out; + } + } } if (verbose) { char features_buf[64]; + update_chunk_allocation(fs_info, &allocation); printf("Label: %s\n", label); printf("UUID: %s\n", mkfs_cfg.fs_uuid); printf("Node size: %u\n", nodesize); @@ -1333,6 +1295,12 @@ out: } } + if (!ret && close_ret) { + ret = close_ret; + error("failed to close ctree, the filesystem may be inconsistent: %d", + ret); + } + btrfs_close_all_devices(); free(label);