X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=utils.c;h=715bab0ebfb56e91b92338385bd5838add274235;hb=7cdd58b2e92863f055c70ef7134d3a70760473c7;hp=f64f3fd2d7231f8780a646c99d6b1bee451b2cc3;hpb=1ed3426d3f74bbbfaab2b77991596644e2e6321d;p=platform%2Fupstream%2Fbtrfs-progs.git diff --git a/utils.c b/utils.c index f64f3fd..715bab0 100644 --- a/utils.c +++ b/utils.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -39,6 +40,8 @@ #include #include +#include + #include "kerncompat.h" #include "radix-tree.h" #include "ctree.h" @@ -49,6 +52,7 @@ #include "volumes.h" #include "ioctl.h" #include "commands.h" +#include "mkfs/common.h" #ifndef BLKDISCARD #define BLKDISCARD _IO(0x12,119) @@ -56,60 +60,10 @@ static int btrfs_scan_done = 0; -static char argv0_buf[ARGV0_BUF_SIZE] = "btrfs"; - static int rand_seed_initlized = 0; static unsigned short rand_seed[3]; -const char *get_argv0_buf(void) -{ - return argv0_buf; -} - -void fixup_argv0(char **argv, const char *token) -{ - int len = strlen(argv0_buf); - - snprintf(argv0_buf + len, sizeof(argv0_buf) - len, " %s", token); - argv[0] = argv0_buf; -} - -void set_argv0(char **argv) -{ - strncpy(argv0_buf, argv[0], sizeof(argv0_buf)); - argv0_buf[sizeof(argv0_buf) - 1] = 0; -} - -int check_argc_exact(int nargs, int expected) -{ - if (nargs < expected) - fprintf(stderr, "%s: too few arguments\n", argv0_buf); - if (nargs > expected) - fprintf(stderr, "%s: too many arguments\n", argv0_buf); - - return nargs != expected; -} - -int check_argc_min(int nargs, int expected) -{ - if (nargs < expected) { - fprintf(stderr, "%s: too few arguments\n", argv0_buf); - return 1; - } - - return 0; -} - -int check_argc_max(int nargs, int expected) -{ - if (nargs > expected) { - fprintf(stderr, "%s: too many arguments\n", argv0_buf); - return 1; - } - - return 0; -} - +struct btrfs_config bconf; /* * Discard the given range in one go @@ -130,7 +84,7 @@ static int discard_blocks(int fd, u64 start, u64 len) { while (len > 0) { /* 1G granularity */ - u64 chunk_size = min_t(u64, len, 1*1024*1024*1024); + u64 chunk_size = min_t(u64, len, SZ_1G); int ret; ret = discard_range(fd, start, chunk_size); @@ -143,15 +97,6 @@ static int discard_blocks(int fd, u64 start, u64 len) return 0; } -static u64 reference_root_table[] = { - [1] = BTRFS_ROOT_TREE_OBJECTID, - [2] = BTRFS_EXTENT_TREE_OBJECTID, - [3] = BTRFS_CHUNK_TREE_OBJECTID, - [4] = BTRFS_DEV_TREE_OBJECTID, - [5] = BTRFS_FS_TREE_OBJECTID, - [6] = BTRFS_CSUM_TREE_OBJECTID, -}; - int test_uuid_unique(char *fs_uuid) { int unique = 1; @@ -181,1380 +126,6 @@ int test_uuid_unique(char *fs_uuid) return unique; } -/* - * Reserve space from free_tree. - * The algorithm is very simple, find the first cache_extent with enough space - * and allocate from its beginning. - */ -static int reserve_free_space(struct cache_tree *free_tree, u64 len, - u64 *ret_start) -{ - struct cache_extent *cache; - int found = 0; - - ASSERT(ret_start != NULL); - cache = first_cache_extent(free_tree); - while (cache) { - if (cache->size > len) { - found = 1; - *ret_start = cache->start; - - cache->size -= len; - if (cache->size == 0) { - remove_cache_extent(free_tree, cache); - free(cache); - } else { - cache->start += len; - } - break; - } - cache = next_cache_extent(cache); - } - if (!found) - return -ENOSPC; - return 0; -} - -static inline int write_temp_super(int fd, struct btrfs_super_block *sb, - u64 sb_bytenr) -{ - u32 crc = ~(u32)0; - int ret; - - crc = btrfs_csum_data(NULL, (char *)sb + BTRFS_CSUM_SIZE, crc, - BTRFS_SUPER_INFO_SIZE - BTRFS_CSUM_SIZE); - btrfs_csum_final(crc, (char *)&sb->csum[0]); - ret = pwrite(fd, sb, BTRFS_SUPER_INFO_SIZE, sb_bytenr); - if (ret < BTRFS_SUPER_INFO_SIZE) - ret = (ret < 0 ? -errno : -EIO); - else - ret = 0; - return ret; -} - -/* - * Setup temporary superblock at cfg->super_bynter - * Needed info are extracted from cfg, and root_bytenr, chunk_bytenr - * - * For now sys chunk array will be empty and dev_item is empty too. - * They will be re-initialized at temp chunk tree setup. - * - * The superblock signature is not valid, denotes a partially created - * filesystem, needs to be finalized. - */ -static int setup_temp_super(int fd, struct btrfs_mkfs_config *cfg, - u64 root_bytenr, u64 chunk_bytenr) -{ - unsigned char chunk_uuid[BTRFS_UUID_SIZE]; - char super_buf[BTRFS_SUPER_INFO_SIZE]; - struct btrfs_super_block *super = (struct btrfs_super_block *)super_buf; - int ret; - - memset(super_buf, 0, BTRFS_SUPER_INFO_SIZE); - cfg->num_bytes = round_down(cfg->num_bytes, cfg->sectorsize); - - if (*cfg->fs_uuid) { - if (uuid_parse(cfg->fs_uuid, super->fsid) != 0) { - error("cound not parse UUID: %s", cfg->fs_uuid); - ret = -EINVAL; - goto out; - } - if (!test_uuid_unique(cfg->fs_uuid)) { - error("non-unique UUID: %s", cfg->fs_uuid); - ret = -EINVAL; - goto out; - } - } else { - uuid_generate(super->fsid); - uuid_unparse(super->fsid, cfg->fs_uuid); - } - uuid_generate(chunk_uuid); - uuid_unparse(chunk_uuid, cfg->chunk_uuid); - - btrfs_set_super_bytenr(super, cfg->super_bytenr); - btrfs_set_super_num_devices(super, 1); - btrfs_set_super_magic(super, BTRFS_MAGIC_PARTIAL); - btrfs_set_super_generation(super, 1); - btrfs_set_super_root(super, root_bytenr); - btrfs_set_super_chunk_root(super, chunk_bytenr); - btrfs_set_super_total_bytes(super, cfg->num_bytes); - /* - * Temporary filesystem will only have 6 tree roots: - * chunk tree, root tree, extent_tree, device tree, fs tree - * and csum tree. - */ - btrfs_set_super_bytes_used(super, 6 * cfg->nodesize); - btrfs_set_super_sectorsize(super, cfg->sectorsize); - btrfs_set_super_leafsize(super, cfg->nodesize); - btrfs_set_super_nodesize(super, cfg->nodesize); - btrfs_set_super_stripesize(super, cfg->stripesize); - btrfs_set_super_csum_type(super, BTRFS_CSUM_TYPE_CRC32); - btrfs_set_super_chunk_root(super, chunk_bytenr); - btrfs_set_super_cache_generation(super, -1); - btrfs_set_super_incompat_flags(super, cfg->features); - if (cfg->label) - __strncpy_null(super->label, cfg->label, BTRFS_LABEL_SIZE - 1); - - /* Sys chunk array will be re-initialized at chunk tree init time */ - super->sys_chunk_array_size = 0; - - ret = write_temp_super(fd, super, cfg->super_bytenr); -out: - return ret; -} - -/* - * Setup an extent buffer for tree block. - */ -static int setup_temp_extent_buffer(struct extent_buffer *buf, - struct btrfs_mkfs_config *cfg, - u64 bytenr, u64 owner) -{ - unsigned char fsid[BTRFS_FSID_SIZE]; - unsigned char chunk_uuid[BTRFS_UUID_SIZE]; - int ret; - - ret = uuid_parse(cfg->fs_uuid, fsid); - if (ret) - return -EINVAL; - ret = uuid_parse(cfg->chunk_uuid, chunk_uuid); - if (ret) - return -EINVAL; - - memset(buf->data, 0, cfg->nodesize); - buf->len = cfg->nodesize; - btrfs_set_header_bytenr(buf, bytenr); - btrfs_set_header_generation(buf, 1); - btrfs_set_header_backref_rev(buf, BTRFS_MIXED_BACKREF_REV); - btrfs_set_header_owner(buf, owner); - btrfs_set_header_flags(buf, BTRFS_HEADER_FLAG_WRITTEN); - write_extent_buffer(buf, chunk_uuid, btrfs_header_chunk_tree_uuid(buf), - BTRFS_UUID_SIZE); - write_extent_buffer(buf, fsid, btrfs_header_fsid(), BTRFS_FSID_SIZE); - return 0; -} - -static inline int write_temp_extent_buffer(int fd, struct extent_buffer *buf, - u64 bytenr) -{ - int ret; - - csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0); - - /* Temporary extent buffer is always mapped 1:1 on disk */ - ret = pwrite(fd, buf->data, buf->len, bytenr); - if (ret < buf->len) - ret = (ret < 0 ? ret : -EIO); - else - ret = 0; - return ret; -} - -/* - * Insert a root item for temporary tree root - * - * Only used in make_btrfs_v2(). - */ -static void insert_temp_root_item(struct extent_buffer *buf, - struct btrfs_mkfs_config *cfg, - int *slot, u32 *itemoff, u64 objectid, - u64 bytenr) -{ - struct btrfs_root_item root_item; - struct btrfs_inode_item *inode_item; - struct btrfs_disk_key disk_key; - - btrfs_set_header_nritems(buf, *slot + 1); - (*itemoff) -= sizeof(root_item); - memset(&root_item, 0, sizeof(root_item)); - inode_item = &root_item.inode; - btrfs_set_stack_inode_generation(inode_item, 1); - btrfs_set_stack_inode_size(inode_item, 3); - btrfs_set_stack_inode_nlink(inode_item, 1); - btrfs_set_stack_inode_nbytes(inode_item, cfg->nodesize); - btrfs_set_stack_inode_mode(inode_item, S_IFDIR | 0755); - btrfs_set_root_refs(&root_item, 1); - btrfs_set_root_used(&root_item, cfg->nodesize); - btrfs_set_root_generation(&root_item, 1); - btrfs_set_root_bytenr(&root_item, bytenr); - - memset(&disk_key, 0, sizeof(disk_key)); - btrfs_set_disk_key_type(&disk_key, BTRFS_ROOT_ITEM_KEY); - btrfs_set_disk_key_objectid(&disk_key, objectid); - btrfs_set_disk_key_offset(&disk_key, 0); - - btrfs_set_item_key(buf, &disk_key, *slot); - btrfs_set_item_offset(buf, btrfs_item_nr(*slot), *itemoff); - btrfs_set_item_size(buf, btrfs_item_nr(*slot), sizeof(root_item)); - write_extent_buffer(buf, &root_item, - btrfs_item_ptr_offset(buf, *slot), - sizeof(root_item)); - (*slot)++; -} - -static int setup_temp_root_tree(int fd, struct btrfs_mkfs_config *cfg, - u64 root_bytenr, u64 extent_bytenr, - u64 dev_bytenr, u64 fs_bytenr, u64 csum_bytenr) -{ - struct extent_buffer *buf = NULL; - u32 itemoff = __BTRFS_LEAF_DATA_SIZE(cfg->nodesize); - int slot = 0; - int ret; - - /* - * Provided bytenr must in ascending order, or tree root will have a - * bad key order. - */ - if (!(root_bytenr < extent_bytenr && extent_bytenr < dev_bytenr && - dev_bytenr < fs_bytenr && fs_bytenr < csum_bytenr)) { - error("bad tree bytenr order: " - "root < extent %llu < %llu, " - "extent < dev %llu < %llu, " - "dev < fs %llu < %llu, " - "fs < csum %llu < %llu", - (unsigned long long)root_bytenr, - (unsigned long long)extent_bytenr, - (unsigned long long)extent_bytenr, - (unsigned long long)dev_bytenr, - (unsigned long long)dev_bytenr, - (unsigned long long)fs_bytenr, - (unsigned long long)fs_bytenr, - (unsigned long long)csum_bytenr); - return -EINVAL; - } - buf = malloc(sizeof(*buf) + cfg->nodesize); - if (!buf) - return -ENOMEM; - - ret = setup_temp_extent_buffer(buf, cfg, root_bytenr, - BTRFS_ROOT_TREE_OBJECTID); - if (ret < 0) - goto out; - - insert_temp_root_item(buf, cfg, &slot, &itemoff, - BTRFS_EXTENT_TREE_OBJECTID, extent_bytenr); - insert_temp_root_item(buf, cfg, &slot, &itemoff, - BTRFS_DEV_TREE_OBJECTID, dev_bytenr); - insert_temp_root_item(buf, cfg, &slot, &itemoff, - BTRFS_FS_TREE_OBJECTID, fs_bytenr); - insert_temp_root_item(buf, cfg, &slot, &itemoff, - BTRFS_CSUM_TREE_OBJECTID, csum_bytenr); - - ret = write_temp_extent_buffer(fd, buf, root_bytenr); -out: - free(buf); - return ret; -} - -static int insert_temp_dev_item(int fd, struct extent_buffer *buf, - struct btrfs_mkfs_config *cfg, - int *slot, u32 *itemoff) -{ - struct btrfs_disk_key disk_key; - struct btrfs_dev_item *dev_item; - char super_buf[BTRFS_SUPER_INFO_SIZE]; - unsigned char dev_uuid[BTRFS_UUID_SIZE]; - unsigned char fsid[BTRFS_FSID_SIZE]; - struct btrfs_super_block *super = (struct btrfs_super_block *)super_buf; - int ret; - - ret = pread(fd, super_buf, BTRFS_SUPER_INFO_SIZE, cfg->super_bytenr); - if (ret < BTRFS_SUPER_INFO_SIZE) { - ret = (ret < 0 ? -errno : -EIO); - goto out; - } - - btrfs_set_header_nritems(buf, *slot + 1); - (*itemoff) -= sizeof(*dev_item); - /* setup device item 1, 0 is for replace case */ - btrfs_set_disk_key_type(&disk_key, BTRFS_DEV_ITEM_KEY); - btrfs_set_disk_key_objectid(&disk_key, BTRFS_DEV_ITEMS_OBJECTID); - btrfs_set_disk_key_offset(&disk_key, 1); - btrfs_set_item_key(buf, &disk_key, *slot); - btrfs_set_item_offset(buf, btrfs_item_nr(*slot), *itemoff); - btrfs_set_item_size(buf, btrfs_item_nr(*slot), sizeof(*dev_item)); - - dev_item = btrfs_item_ptr(buf, *slot, struct btrfs_dev_item); - /* Generate device uuid */ - uuid_generate(dev_uuid); - write_extent_buffer(buf, dev_uuid, - (unsigned long)btrfs_device_uuid(dev_item), - BTRFS_UUID_SIZE); - uuid_parse(cfg->fs_uuid, fsid); - write_extent_buffer(buf, fsid, - (unsigned long)btrfs_device_fsid(dev_item), - BTRFS_FSID_SIZE); - btrfs_set_device_id(buf, dev_item, 1); - btrfs_set_device_generation(buf, dev_item, 0); - btrfs_set_device_total_bytes(buf, dev_item, cfg->num_bytes); - /* - * The number must match the initial SYSTEM and META chunk size - */ - btrfs_set_device_bytes_used(buf, dev_item, - BTRFS_MKFS_SYSTEM_GROUP_SIZE + - BTRFS_CONVERT_META_GROUP_SIZE); - btrfs_set_device_io_align(buf, dev_item, cfg->sectorsize); - btrfs_set_device_io_width(buf, dev_item, cfg->sectorsize); - btrfs_set_device_sector_size(buf, dev_item, cfg->sectorsize); - btrfs_set_device_type(buf, dev_item, 0); - - /* Super dev_item is not complete, copy the complete one to sb */ - read_extent_buffer(buf, &super->dev_item, (unsigned long)dev_item, - sizeof(*dev_item)); - ret = write_temp_super(fd, super, cfg->super_bytenr); - (*slot)++; -out: - return ret; -} - -static int insert_temp_chunk_item(int fd, struct extent_buffer *buf, - struct btrfs_mkfs_config *cfg, - int *slot, u32 *itemoff, u64 start, u64 len, - u64 type) -{ - struct btrfs_chunk *chunk; - struct btrfs_disk_key disk_key; - char super_buf[BTRFS_SUPER_INFO_SIZE]; - struct btrfs_super_block *sb = (struct btrfs_super_block *)super_buf; - int ret = 0; - - ret = pread(fd, super_buf, BTRFS_SUPER_INFO_SIZE, - cfg->super_bytenr); - if (ret < BTRFS_SUPER_INFO_SIZE) { - ret = (ret < 0 ? ret : -EIO); - return ret; - } - - btrfs_set_header_nritems(buf, *slot + 1); - (*itemoff) -= btrfs_chunk_item_size(1); - btrfs_set_disk_key_type(&disk_key, BTRFS_CHUNK_ITEM_KEY); - btrfs_set_disk_key_objectid(&disk_key, BTRFS_FIRST_CHUNK_TREE_OBJECTID); - btrfs_set_disk_key_offset(&disk_key, start); - btrfs_set_item_key(buf, &disk_key, *slot); - btrfs_set_item_offset(buf, btrfs_item_nr(*slot), *itemoff); - btrfs_set_item_size(buf, btrfs_item_nr(*slot), - btrfs_chunk_item_size(1)); - - chunk = btrfs_item_ptr(buf, *slot, struct btrfs_chunk); - btrfs_set_chunk_length(buf, chunk, len); - btrfs_set_chunk_owner(buf, chunk, BTRFS_EXTENT_TREE_OBJECTID); - btrfs_set_chunk_stripe_len(buf, chunk, 64 * 1024); - btrfs_set_chunk_type(buf, chunk, type); - btrfs_set_chunk_io_align(buf, chunk, cfg->sectorsize); - btrfs_set_chunk_io_width(buf, chunk, cfg->sectorsize); - btrfs_set_chunk_sector_size(buf, chunk, cfg->sectorsize); - btrfs_set_chunk_num_stripes(buf, chunk, 1); - /* TODO: Support DUP profile for system chunk */ - btrfs_set_stripe_devid_nr(buf, chunk, 0, 1); - /* We are doing 1:1 mapping, so start is its dev offset */ - btrfs_set_stripe_offset_nr(buf, chunk, 0, start); - write_extent_buffer(buf, &sb->dev_item.uuid, - (unsigned long)btrfs_stripe_dev_uuid_nr(chunk, 0), - BTRFS_UUID_SIZE); - (*slot)++; - - /* - * If it's system chunk, also copy it to super block. - */ - if (type & BTRFS_BLOCK_GROUP_SYSTEM) { - char *cur; - - cur = (char *)sb->sys_chunk_array + sb->sys_chunk_array_size; - memcpy(cur, &disk_key, sizeof(disk_key)); - cur += sizeof(disk_key); - read_extent_buffer(buf, cur, (unsigned long int)chunk, - btrfs_chunk_item_size(1)); - sb->sys_chunk_array_size += btrfs_chunk_item_size(1) + - sizeof(disk_key); - - ret = write_temp_super(fd, sb, cfg->super_bytenr); - } - return ret; -} - -static int setup_temp_chunk_tree(int fd, struct btrfs_mkfs_config *cfg, - u64 sys_chunk_start, u64 meta_chunk_start, - u64 chunk_bytenr) -{ - struct extent_buffer *buf = NULL; - u32 itemoff = __BTRFS_LEAF_DATA_SIZE(cfg->nodesize); - int slot = 0; - int ret; - - /* Must ensure SYS chunk starts before META chunk */ - if (meta_chunk_start < sys_chunk_start) { - error("wrong chunk order: meta < system %llu < %llu", - (unsigned long long)meta_chunk_start, - (unsigned long long)sys_chunk_start); - return -EINVAL; - } - buf = malloc(sizeof(*buf) + cfg->nodesize); - if (!buf) - return -ENOMEM; - ret = setup_temp_extent_buffer(buf, cfg, chunk_bytenr, - BTRFS_CHUNK_TREE_OBJECTID); - if (ret < 0) - goto out; - - ret = insert_temp_dev_item(fd, buf, cfg, &slot, &itemoff); - if (ret < 0) - goto out; - ret = insert_temp_chunk_item(fd, buf, cfg, &slot, &itemoff, - sys_chunk_start, - BTRFS_MKFS_SYSTEM_GROUP_SIZE, - BTRFS_BLOCK_GROUP_SYSTEM); - if (ret < 0) - goto out; - ret = insert_temp_chunk_item(fd, buf, cfg, &slot, &itemoff, - meta_chunk_start, - BTRFS_CONVERT_META_GROUP_SIZE, - BTRFS_BLOCK_GROUP_METADATA); - if (ret < 0) - goto out; - ret = write_temp_extent_buffer(fd, buf, chunk_bytenr); - -out: - free(buf); - return ret; -} - -static void insert_temp_dev_extent(struct extent_buffer *buf, - int *slot, u32 *itemoff, u64 start, u64 len) -{ - struct btrfs_dev_extent *dev_extent; - struct btrfs_disk_key disk_key; - - btrfs_set_header_nritems(buf, *slot + 1); - (*itemoff) -= sizeof(*dev_extent); - btrfs_set_disk_key_type(&disk_key, BTRFS_DEV_EXTENT_KEY); - btrfs_set_disk_key_objectid(&disk_key, 1); - btrfs_set_disk_key_offset(&disk_key, start); - btrfs_set_item_key(buf, &disk_key, *slot); - btrfs_set_item_offset(buf, btrfs_item_nr(*slot), *itemoff); - btrfs_set_item_size(buf, btrfs_item_nr(*slot), sizeof(*dev_extent)); - - dev_extent = btrfs_item_ptr(buf, *slot, struct btrfs_dev_extent); - btrfs_set_dev_extent_chunk_objectid(buf, dev_extent, - BTRFS_FIRST_CHUNK_TREE_OBJECTID); - btrfs_set_dev_extent_length(buf, dev_extent, len); - btrfs_set_dev_extent_chunk_offset(buf, dev_extent, start); - btrfs_set_dev_extent_chunk_tree(buf, dev_extent, - BTRFS_CHUNK_TREE_OBJECTID); - (*slot)++; -} - -static int setup_temp_dev_tree(int fd, struct btrfs_mkfs_config *cfg, - u64 sys_chunk_start, u64 meta_chunk_start, - u64 dev_bytenr) -{ - struct extent_buffer *buf = NULL; - u32 itemoff = __BTRFS_LEAF_DATA_SIZE(cfg->nodesize); - int slot = 0; - int ret; - - /* Must ensure SYS chunk starts before META chunk */ - if (meta_chunk_start < sys_chunk_start) { - error("wrong chunk order: meta < system %llu < %llu", - (unsigned long long)meta_chunk_start, - (unsigned long long)sys_chunk_start); - return -EINVAL; - } - buf = malloc(sizeof(*buf) + cfg->nodesize); - if (!buf) - return -ENOMEM; - ret = setup_temp_extent_buffer(buf, cfg, dev_bytenr, - BTRFS_DEV_TREE_OBJECTID); - if (ret < 0) - goto out; - insert_temp_dev_extent(buf, &slot, &itemoff, sys_chunk_start, - BTRFS_MKFS_SYSTEM_GROUP_SIZE); - insert_temp_dev_extent(buf, &slot, &itemoff, meta_chunk_start, - BTRFS_CONVERT_META_GROUP_SIZE); - ret = write_temp_extent_buffer(fd, buf, dev_bytenr); -out: - free(buf); - return ret; -} - -static int setup_temp_fs_tree(int fd, struct btrfs_mkfs_config *cfg, - u64 fs_bytenr) -{ - struct extent_buffer *buf = NULL; - int ret; - - buf = malloc(sizeof(*buf) + cfg->nodesize); - if (!buf) - return -ENOMEM; - ret = setup_temp_extent_buffer(buf, cfg, fs_bytenr, - BTRFS_FS_TREE_OBJECTID); - if (ret < 0) - goto out; - /* - * Temporary fs tree is completely empty. - */ - ret = write_temp_extent_buffer(fd, buf, fs_bytenr); -out: - free(buf); - return ret; -} - -static int setup_temp_csum_tree(int fd, struct btrfs_mkfs_config *cfg, - u64 csum_bytenr) -{ - struct extent_buffer *buf = NULL; - int ret; - - buf = malloc(sizeof(*buf) + cfg->nodesize); - if (!buf) - return -ENOMEM; - ret = setup_temp_extent_buffer(buf, cfg, csum_bytenr, - BTRFS_CSUM_TREE_OBJECTID); - if (ret < 0) - goto out; - /* - * Temporary csum tree is completely empty. - */ - ret = write_temp_extent_buffer(fd, buf, csum_bytenr); -out: - free(buf); - return ret; -} - -/* - * Insert one temporary extent item. - * - * NOTE: if skinny_metadata is not enabled, this function must be called - * after all other trees are initialized. - * Or fs without skinny-metadata will be screwed up. - */ -static int insert_temp_extent_item(int fd, struct extent_buffer *buf, - struct btrfs_mkfs_config *cfg, - int *slot, u32 *itemoff, u64 bytenr, - u64 ref_root) -{ - struct extent_buffer *tmp; - struct btrfs_extent_item *ei; - struct btrfs_extent_inline_ref *iref; - struct btrfs_disk_key disk_key; - struct btrfs_disk_key tree_info_key; - struct btrfs_tree_block_info *info; - int itemsize; - int skinny_metadata = cfg->features & - BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA; - int ret; - - if (skinny_metadata) - itemsize = sizeof(*ei) + sizeof(*iref); - else - itemsize = sizeof(*ei) + sizeof(*iref) + - sizeof(struct btrfs_tree_block_info); - - btrfs_set_header_nritems(buf, *slot + 1); - *(itemoff) -= itemsize; - - if (skinny_metadata) { - btrfs_set_disk_key_type(&disk_key, BTRFS_METADATA_ITEM_KEY); - btrfs_set_disk_key_offset(&disk_key, 0); - } else { - btrfs_set_disk_key_type(&disk_key, BTRFS_EXTENT_ITEM_KEY); - btrfs_set_disk_key_offset(&disk_key, cfg->nodesize); - } - btrfs_set_disk_key_objectid(&disk_key, bytenr); - - btrfs_set_item_key(buf, &disk_key, *slot); - btrfs_set_item_offset(buf, btrfs_item_nr(*slot), *itemoff); - btrfs_set_item_size(buf, btrfs_item_nr(*slot), itemsize); - - ei = btrfs_item_ptr(buf, *slot, struct btrfs_extent_item); - btrfs_set_extent_refs(buf, ei, 1); - btrfs_set_extent_generation(buf, ei, 1); - btrfs_set_extent_flags(buf, ei, BTRFS_EXTENT_FLAG_TREE_BLOCK); - - if (skinny_metadata) { - iref = (struct btrfs_extent_inline_ref *)(ei + 1); - } else { - info = (struct btrfs_tree_block_info *)(ei + 1); - iref = (struct btrfs_extent_inline_ref *)(info + 1); - } - btrfs_set_extent_inline_ref_type(buf, iref, - BTRFS_TREE_BLOCK_REF_KEY); - btrfs_set_extent_inline_ref_offset(buf, iref, ref_root); - - (*slot)++; - if (skinny_metadata) - return 0; - - /* - * Lastly, check the tree block key by read the tree block - * Since we do 1:1 mapping for convert case, we can directly - * read the bytenr from disk - */ - tmp = malloc(sizeof(*tmp) + cfg->nodesize); - if (!tmp) - return -ENOMEM; - ret = setup_temp_extent_buffer(tmp, cfg, bytenr, ref_root); - if (ret < 0) - goto out; - ret = pread(fd, tmp->data, cfg->nodesize, bytenr); - if (ret < cfg->nodesize) { - ret = (ret < 0 ? -errno : -EIO); - goto out; - } - if (btrfs_header_nritems(tmp) == 0) { - btrfs_set_disk_key_type(&tree_info_key, 0); - btrfs_set_disk_key_objectid(&tree_info_key, 0); - btrfs_set_disk_key_offset(&tree_info_key, 0); - } else { - btrfs_item_key(tmp, &tree_info_key, 0); - } - btrfs_set_tree_block_key(buf, info, &tree_info_key); - -out: - free(tmp); - return ret; -} - -static void insert_temp_block_group(struct extent_buffer *buf, - struct btrfs_mkfs_config *cfg, - int *slot, u32 *itemoff, - u64 bytenr, u64 len, u64 used, u64 flag) -{ - struct btrfs_block_group_item bgi; - struct btrfs_disk_key disk_key; - - btrfs_set_header_nritems(buf, *slot + 1); - (*itemoff) -= sizeof(bgi); - btrfs_set_disk_key_type(&disk_key, BTRFS_BLOCK_GROUP_ITEM_KEY); - btrfs_set_disk_key_objectid(&disk_key, bytenr); - btrfs_set_disk_key_offset(&disk_key, len); - btrfs_set_item_key(buf, &disk_key, *slot); - btrfs_set_item_offset(buf, btrfs_item_nr(*slot), *itemoff); - btrfs_set_item_size(buf, btrfs_item_nr(*slot), sizeof(bgi)); - - btrfs_set_block_group_flags(&bgi, flag); - btrfs_set_block_group_used(&bgi, used); - btrfs_set_block_group_chunk_objectid(&bgi, - BTRFS_FIRST_CHUNK_TREE_OBJECTID); - write_extent_buffer(buf, &bgi, btrfs_item_ptr_offset(buf, *slot), - sizeof(bgi)); - (*slot)++; -} - -static int setup_temp_extent_tree(int fd, struct btrfs_mkfs_config *cfg, - u64 chunk_bytenr, u64 root_bytenr, - u64 extent_bytenr, u64 dev_bytenr, - u64 fs_bytenr, u64 csum_bytenr) -{ - struct extent_buffer *buf = NULL; - u32 itemoff = __BTRFS_LEAF_DATA_SIZE(cfg->nodesize); - int slot = 0; - int ret; - - /* - * We must ensure provided bytenr are in ascending order, - * or extent tree key order will be broken. - */ - if (!(chunk_bytenr < root_bytenr && root_bytenr < extent_bytenr && - extent_bytenr < dev_bytenr && dev_bytenr < fs_bytenr && - fs_bytenr < csum_bytenr)) { - error("bad tree bytenr order: " - "chunk < root %llu < %llu, " - "root < extent %llu < %llu, " - "extent < dev %llu < %llu, " - "dev < fs %llu < %llu, " - "fs < csum %llu < %llu", - (unsigned long long)chunk_bytenr, - (unsigned long long)root_bytenr, - (unsigned long long)root_bytenr, - (unsigned long long)extent_bytenr, - (unsigned long long)extent_bytenr, - (unsigned long long)dev_bytenr, - (unsigned long long)dev_bytenr, - (unsigned long long)fs_bytenr, - (unsigned long long)fs_bytenr, - (unsigned long long)csum_bytenr); - return -EINVAL; - } - buf = malloc(sizeof(*buf) + cfg->nodesize); - if (!buf) - return -ENOMEM; - - ret = setup_temp_extent_buffer(buf, cfg, extent_bytenr, - BTRFS_EXTENT_TREE_OBJECTID); - if (ret < 0) - goto out; - - ret = insert_temp_extent_item(fd, buf, cfg, &slot, &itemoff, - chunk_bytenr, BTRFS_CHUNK_TREE_OBJECTID); - if (ret < 0) - goto out; - - insert_temp_block_group(buf, cfg, &slot, &itemoff, chunk_bytenr, - BTRFS_MKFS_SYSTEM_GROUP_SIZE, cfg->nodesize, - BTRFS_BLOCK_GROUP_SYSTEM); - - ret = insert_temp_extent_item(fd, buf, cfg, &slot, &itemoff, - root_bytenr, BTRFS_ROOT_TREE_OBJECTID); - if (ret < 0) - goto out; - - /* 5 tree block used, root, extent, dev, fs and csum*/ - insert_temp_block_group(buf, cfg, &slot, &itemoff, root_bytenr, - BTRFS_CONVERT_META_GROUP_SIZE, cfg->nodesize * 5, - BTRFS_BLOCK_GROUP_METADATA); - - ret = insert_temp_extent_item(fd, buf, cfg, &slot, &itemoff, - extent_bytenr, BTRFS_EXTENT_TREE_OBJECTID); - if (ret < 0) - goto out; - ret = insert_temp_extent_item(fd, buf, cfg, &slot, &itemoff, - dev_bytenr, BTRFS_DEV_TREE_OBJECTID); - if (ret < 0) - goto out; - ret = insert_temp_extent_item(fd, buf, cfg, &slot, &itemoff, - fs_bytenr, BTRFS_FS_TREE_OBJECTID); - if (ret < 0) - goto out; - ret = insert_temp_extent_item(fd, buf, cfg, &slot, &itemoff, - csum_bytenr, BTRFS_CSUM_TREE_OBJECTID); - if (ret < 0) - goto out; - - ret = write_temp_extent_buffer(fd, buf, extent_bytenr); -out: - free(buf); - return ret; -} - -/* - * Improved version of make_btrfs(). - * - * This one will - * 1) Do chunk allocation to avoid used data - * And after this function, extent type matches chunk type - * 2) Better structured code - * No super long hand written codes to initialized all tree blocks - * Split into small blocks and reuse codes. - * TODO: Reuse tree operation facilities by introducing new flags - */ -static int make_convert_btrfs(int fd, struct btrfs_mkfs_config *cfg, - struct btrfs_convert_context *cctx) -{ - struct cache_tree *free = &cctx->free; - struct cache_tree *used = &cctx->used; - u64 sys_chunk_start; - u64 meta_chunk_start; - /* chunk tree bytenr, in system chunk */ - u64 chunk_bytenr; - /* metadata trees bytenr, in metadata chunk */ - u64 root_bytenr; - u64 extent_bytenr; - u64 dev_bytenr; - u64 fs_bytenr; - u64 csum_bytenr; - int ret; - - /* Shouldn't happen */ - BUG_ON(cache_tree_empty(used)); - - /* - * reserve space for temporary superblock first - * Here we allocate a little larger space, to keep later - * free space will be STRIPE_LEN aligned - */ - ret = reserve_free_space(free, BTRFS_STRIPE_LEN, - &cfg->super_bytenr); - if (ret < 0) - goto out; - - /* - * Then reserve system chunk space - * TODO: Change system group size depending on cctx->total_bytes. - * If using current 4M, it can only handle less than one TB for - * worst case and then run out of sys space. - */ - ret = reserve_free_space(free, BTRFS_MKFS_SYSTEM_GROUP_SIZE, - &sys_chunk_start); - if (ret < 0) - goto out; - ret = reserve_free_space(free, BTRFS_CONVERT_META_GROUP_SIZE, - &meta_chunk_start); - if (ret < 0) - goto out; - - /* - * Allocated meta/sys chunks will be mapped 1:1 with device offset. - * - * Inside the allocated metadata chunk, the layout will be: - * | offset | contents | - * ------------------------------------- - * | +0 | tree root | - * | +nodesize | extent root | - * | +nodesize * 2 | device root | - * | +nodesize * 3 | fs tree | - * | +nodesize * 4 | csum tree | - * ------------------------------------- - * Inside the allocated system chunk, the layout will be: - * | offset | contents | - * ------------------------------------- - * | +0 | chunk root | - * ------------------------------------- - */ - chunk_bytenr = sys_chunk_start; - root_bytenr = meta_chunk_start; - extent_bytenr = meta_chunk_start + cfg->nodesize; - dev_bytenr = meta_chunk_start + cfg->nodesize * 2; - fs_bytenr = meta_chunk_start + cfg->nodesize * 3; - csum_bytenr = meta_chunk_start + cfg->nodesize * 4; - - ret = setup_temp_super(fd, cfg, root_bytenr, chunk_bytenr); - if (ret < 0) - goto out; - - ret = setup_temp_root_tree(fd, cfg, root_bytenr, extent_bytenr, - dev_bytenr, fs_bytenr, csum_bytenr); - if (ret < 0) - goto out; - ret = setup_temp_chunk_tree(fd, cfg, sys_chunk_start, meta_chunk_start, - chunk_bytenr); - if (ret < 0) - goto out; - ret = setup_temp_dev_tree(fd, cfg, sys_chunk_start, meta_chunk_start, - dev_bytenr); - if (ret < 0) - goto out; - ret = setup_temp_fs_tree(fd, cfg, fs_bytenr); - if (ret < 0) - goto out; - ret = setup_temp_csum_tree(fd, cfg, csum_bytenr); - if (ret < 0) - goto out; - /* - * Setup extent tree last, since it may need to read tree block key - * for non-skinny metadata case. - */ - ret = setup_temp_extent_tree(fd, cfg, chunk_bytenr, root_bytenr, - extent_bytenr, dev_bytenr, fs_bytenr, - csum_bytenr); -out: - return ret; -} - -/* - * @fs_uuid - if NULL, generates a UUID, returns back the new filesystem UUID - * - * The superblock signature is not valid, denotes a partially created - * filesystem, needs to be finalized. - */ -int make_btrfs(int fd, struct btrfs_mkfs_config *cfg, - struct btrfs_convert_context *cctx) -{ - struct btrfs_super_block super; - struct extent_buffer *buf; - struct btrfs_root_item root_item; - struct btrfs_disk_key disk_key; - struct btrfs_extent_item *extent_item; - struct btrfs_inode_item *inode_item; - struct btrfs_chunk *chunk; - struct btrfs_dev_item *dev_item; - struct btrfs_dev_extent *dev_extent; - u8 chunk_tree_uuid[BTRFS_UUID_SIZE]; - u8 *ptr; - int i; - int ret; - u32 itemoff; - u32 nritems = 0; - u64 first_free; - u64 ref_root; - u32 array_size; - u32 item_size; - int skinny_metadata = !!(cfg->features & - BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA); - u64 num_bytes; - - if (cctx) - return make_convert_btrfs(fd, cfg, cctx); - buf = malloc(sizeof(*buf) + max(cfg->sectorsize, cfg->nodesize)); - if (!buf) - return -ENOMEM; - - first_free = BTRFS_SUPER_INFO_OFFSET + cfg->sectorsize * 2 - 1; - first_free &= ~((u64)cfg->sectorsize - 1); - - memset(&super, 0, sizeof(super)); - - num_bytes = (cfg->num_bytes / cfg->sectorsize) * cfg->sectorsize; - if (*cfg->fs_uuid) { - if (uuid_parse(cfg->fs_uuid, super.fsid) != 0) { - error("cannot not parse UUID: %s", cfg->fs_uuid); - ret = -EINVAL; - goto out; - } - if (!test_uuid_unique(cfg->fs_uuid)) { - error("non-unique UUID: %s", cfg->fs_uuid); - ret = -EBUSY; - goto out; - } - } else { - uuid_generate(super.fsid); - if (cfg->fs_uuid) - uuid_unparse(super.fsid, cfg->fs_uuid); - } - uuid_generate(super.dev_item.uuid); - uuid_generate(chunk_tree_uuid); - - btrfs_set_super_bytenr(&super, cfg->blocks[0]); - btrfs_set_super_num_devices(&super, 1); - btrfs_set_super_magic(&super, BTRFS_MAGIC_PARTIAL); - btrfs_set_super_generation(&super, 1); - btrfs_set_super_root(&super, cfg->blocks[1]); - btrfs_set_super_chunk_root(&super, cfg->blocks[3]); - btrfs_set_super_total_bytes(&super, num_bytes); - btrfs_set_super_bytes_used(&super, 6 * cfg->nodesize); - btrfs_set_super_sectorsize(&super, cfg->sectorsize); - btrfs_set_super_leafsize(&super, cfg->nodesize); - btrfs_set_super_nodesize(&super, cfg->nodesize); - btrfs_set_super_stripesize(&super, cfg->stripesize); - btrfs_set_super_csum_type(&super, BTRFS_CSUM_TYPE_CRC32); - btrfs_set_super_chunk_root_generation(&super, 1); - btrfs_set_super_cache_generation(&super, -1); - btrfs_set_super_incompat_flags(&super, cfg->features); - if (cfg->label) - __strncpy_null(super.label, cfg->label, BTRFS_LABEL_SIZE - 1); - - /* create the tree of root objects */ - memset(buf->data, 0, cfg->nodesize); - buf->len = cfg->nodesize; - btrfs_set_header_bytenr(buf, cfg->blocks[1]); - btrfs_set_header_nritems(buf, 4); - btrfs_set_header_generation(buf, 1); - btrfs_set_header_backref_rev(buf, BTRFS_MIXED_BACKREF_REV); - btrfs_set_header_owner(buf, BTRFS_ROOT_TREE_OBJECTID); - write_extent_buffer(buf, super.fsid, btrfs_header_fsid(), - BTRFS_FSID_SIZE); - - write_extent_buffer(buf, chunk_tree_uuid, - btrfs_header_chunk_tree_uuid(buf), - BTRFS_UUID_SIZE); - - /* create the items for the root tree */ - memset(&root_item, 0, sizeof(root_item)); - inode_item = &root_item.inode; - btrfs_set_stack_inode_generation(inode_item, 1); - btrfs_set_stack_inode_size(inode_item, 3); - btrfs_set_stack_inode_nlink(inode_item, 1); - btrfs_set_stack_inode_nbytes(inode_item, cfg->nodesize); - btrfs_set_stack_inode_mode(inode_item, S_IFDIR | 0755); - btrfs_set_root_refs(&root_item, 1); - btrfs_set_root_used(&root_item, cfg->nodesize); - btrfs_set_root_generation(&root_item, 1); - - memset(&disk_key, 0, sizeof(disk_key)); - btrfs_set_disk_key_type(&disk_key, BTRFS_ROOT_ITEM_KEY); - btrfs_set_disk_key_offset(&disk_key, 0); - nritems = 0; - - itemoff = __BTRFS_LEAF_DATA_SIZE(cfg->nodesize) - sizeof(root_item); - btrfs_set_root_bytenr(&root_item, cfg->blocks[2]); - btrfs_set_disk_key_objectid(&disk_key, BTRFS_EXTENT_TREE_OBJECTID); - btrfs_set_item_key(buf, &disk_key, nritems); - btrfs_set_item_offset(buf, btrfs_item_nr(nritems), itemoff); - btrfs_set_item_size(buf, btrfs_item_nr(nritems), - sizeof(root_item)); - write_extent_buffer(buf, &root_item, btrfs_item_ptr_offset(buf, - nritems), sizeof(root_item)); - nritems++; - - itemoff = itemoff - sizeof(root_item); - btrfs_set_root_bytenr(&root_item, cfg->blocks[4]); - btrfs_set_disk_key_objectid(&disk_key, BTRFS_DEV_TREE_OBJECTID); - btrfs_set_item_key(buf, &disk_key, nritems); - btrfs_set_item_offset(buf, btrfs_item_nr(nritems), itemoff); - btrfs_set_item_size(buf, btrfs_item_nr(nritems), - sizeof(root_item)); - write_extent_buffer(buf, &root_item, - btrfs_item_ptr_offset(buf, nritems), - sizeof(root_item)); - nritems++; - - itemoff = itemoff - sizeof(root_item); - btrfs_set_root_bytenr(&root_item, cfg->blocks[5]); - btrfs_set_disk_key_objectid(&disk_key, BTRFS_FS_TREE_OBJECTID); - btrfs_set_item_key(buf, &disk_key, nritems); - btrfs_set_item_offset(buf, btrfs_item_nr(nritems), itemoff); - btrfs_set_item_size(buf, btrfs_item_nr(nritems), - sizeof(root_item)); - write_extent_buffer(buf, &root_item, - btrfs_item_ptr_offset(buf, nritems), - sizeof(root_item)); - nritems++; - - itemoff = itemoff - sizeof(root_item); - btrfs_set_root_bytenr(&root_item, cfg->blocks[6]); - btrfs_set_disk_key_objectid(&disk_key, BTRFS_CSUM_TREE_OBJECTID); - btrfs_set_item_key(buf, &disk_key, nritems); - btrfs_set_item_offset(buf, btrfs_item_nr(nritems), itemoff); - btrfs_set_item_size(buf, btrfs_item_nr(nritems), - sizeof(root_item)); - write_extent_buffer(buf, &root_item, - btrfs_item_ptr_offset(buf, nritems), - sizeof(root_item)); - nritems++; - - - csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0); - ret = pwrite(fd, buf->data, cfg->nodesize, cfg->blocks[1]); - if (ret != cfg->nodesize) { - ret = (ret < 0 ? -errno : -EIO); - goto out; - } - - /* create the items for the extent tree */ - memset(buf->data + sizeof(struct btrfs_header), 0, - cfg->nodesize - sizeof(struct btrfs_header)); - nritems = 0; - itemoff = __BTRFS_LEAF_DATA_SIZE(cfg->nodesize); - for (i = 1; i < 7; i++) { - item_size = sizeof(struct btrfs_extent_item); - if (!skinny_metadata) - item_size += sizeof(struct btrfs_tree_block_info); - - if (cfg->blocks[i] < first_free) { - error("block[%d] below first free: %llu < %llu", - i, (unsigned long long)cfg->blocks[i], - (unsigned long long)first_free); - ret = -EINVAL; - goto out; - } - if (cfg->blocks[i] < cfg->blocks[i - 1]) { - error("blocks %d and %d in reverse order: %llu < %llu", - i, i - 1, - (unsigned long long)cfg->blocks[i], - (unsigned long long)cfg->blocks[i - 1]); - ret = -EINVAL; - goto out; - } - - /* create extent item */ - itemoff -= item_size; - btrfs_set_disk_key_objectid(&disk_key, cfg->blocks[i]); - if (skinny_metadata) { - btrfs_set_disk_key_type(&disk_key, - BTRFS_METADATA_ITEM_KEY); - btrfs_set_disk_key_offset(&disk_key, 0); - } else { - btrfs_set_disk_key_type(&disk_key, - BTRFS_EXTENT_ITEM_KEY); - btrfs_set_disk_key_offset(&disk_key, cfg->nodesize); - } - btrfs_set_item_key(buf, &disk_key, nritems); - btrfs_set_item_offset(buf, btrfs_item_nr(nritems), - itemoff); - btrfs_set_item_size(buf, btrfs_item_nr(nritems), - item_size); - extent_item = btrfs_item_ptr(buf, nritems, - struct btrfs_extent_item); - btrfs_set_extent_refs(buf, extent_item, 1); - btrfs_set_extent_generation(buf, extent_item, 1); - btrfs_set_extent_flags(buf, extent_item, - BTRFS_EXTENT_FLAG_TREE_BLOCK); - nritems++; - - /* create extent ref */ - ref_root = reference_root_table[i]; - btrfs_set_disk_key_objectid(&disk_key, cfg->blocks[i]); - btrfs_set_disk_key_offset(&disk_key, ref_root); - btrfs_set_disk_key_type(&disk_key, BTRFS_TREE_BLOCK_REF_KEY); - btrfs_set_item_key(buf, &disk_key, nritems); - btrfs_set_item_offset(buf, btrfs_item_nr(nritems), - itemoff); - btrfs_set_item_size(buf, btrfs_item_nr(nritems), 0); - nritems++; - } - btrfs_set_header_bytenr(buf, cfg->blocks[2]); - btrfs_set_header_owner(buf, BTRFS_EXTENT_TREE_OBJECTID); - btrfs_set_header_nritems(buf, nritems); - csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0); - ret = pwrite(fd, buf->data, cfg->nodesize, cfg->blocks[2]); - if (ret != cfg->nodesize) { - ret = (ret < 0 ? -errno : -EIO); - goto out; - } - - /* create the chunk tree */ - memset(buf->data + sizeof(struct btrfs_header), 0, - cfg->nodesize - sizeof(struct btrfs_header)); - nritems = 0; - item_size = sizeof(*dev_item); - itemoff = __BTRFS_LEAF_DATA_SIZE(cfg->nodesize) - item_size; - - /* first device 1 (there is no device 0) */ - btrfs_set_disk_key_objectid(&disk_key, BTRFS_DEV_ITEMS_OBJECTID); - btrfs_set_disk_key_offset(&disk_key, 1); - btrfs_set_disk_key_type(&disk_key, BTRFS_DEV_ITEM_KEY); - btrfs_set_item_key(buf, &disk_key, nritems); - btrfs_set_item_offset(buf, btrfs_item_nr(nritems), itemoff); - btrfs_set_item_size(buf, btrfs_item_nr(nritems), item_size); - - dev_item = btrfs_item_ptr(buf, nritems, struct btrfs_dev_item); - btrfs_set_device_id(buf, dev_item, 1); - btrfs_set_device_generation(buf, dev_item, 0); - btrfs_set_device_total_bytes(buf, dev_item, num_bytes); - btrfs_set_device_bytes_used(buf, dev_item, - BTRFS_MKFS_SYSTEM_GROUP_SIZE); - btrfs_set_device_io_align(buf, dev_item, cfg->sectorsize); - btrfs_set_device_io_width(buf, dev_item, cfg->sectorsize); - btrfs_set_device_sector_size(buf, dev_item, cfg->sectorsize); - btrfs_set_device_type(buf, dev_item, 0); - - write_extent_buffer(buf, super.dev_item.uuid, - (unsigned long)btrfs_device_uuid(dev_item), - BTRFS_UUID_SIZE); - write_extent_buffer(buf, super.fsid, - (unsigned long)btrfs_device_fsid(dev_item), - BTRFS_UUID_SIZE); - read_extent_buffer(buf, &super.dev_item, (unsigned long)dev_item, - sizeof(*dev_item)); - - nritems++; - item_size = btrfs_chunk_item_size(1); - itemoff = itemoff - item_size; - - /* then we have chunk 0 */ - btrfs_set_disk_key_objectid(&disk_key, BTRFS_FIRST_CHUNK_TREE_OBJECTID); - btrfs_set_disk_key_offset(&disk_key, 0); - btrfs_set_disk_key_type(&disk_key, BTRFS_CHUNK_ITEM_KEY); - btrfs_set_item_key(buf, &disk_key, nritems); - btrfs_set_item_offset(buf, btrfs_item_nr(nritems), itemoff); - btrfs_set_item_size(buf, btrfs_item_nr(nritems), item_size); - - chunk = btrfs_item_ptr(buf, nritems, struct btrfs_chunk); - btrfs_set_chunk_length(buf, chunk, BTRFS_MKFS_SYSTEM_GROUP_SIZE); - btrfs_set_chunk_owner(buf, chunk, BTRFS_EXTENT_TREE_OBJECTID); - btrfs_set_chunk_stripe_len(buf, chunk, 64 * 1024); - btrfs_set_chunk_type(buf, chunk, BTRFS_BLOCK_GROUP_SYSTEM); - btrfs_set_chunk_io_align(buf, chunk, cfg->sectorsize); - btrfs_set_chunk_io_width(buf, chunk, cfg->sectorsize); - btrfs_set_chunk_sector_size(buf, chunk, cfg->sectorsize); - btrfs_set_chunk_num_stripes(buf, chunk, 1); - btrfs_set_stripe_devid_nr(buf, chunk, 0, 1); - btrfs_set_stripe_offset_nr(buf, chunk, 0, 0); - nritems++; - - write_extent_buffer(buf, super.dev_item.uuid, - (unsigned long)btrfs_stripe_dev_uuid(&chunk->stripe), - BTRFS_UUID_SIZE); - - /* copy the key for the chunk to the system array */ - ptr = super.sys_chunk_array; - array_size = sizeof(disk_key); - - memcpy(ptr, &disk_key, sizeof(disk_key)); - ptr += sizeof(disk_key); - - /* copy the chunk to the system array */ - read_extent_buffer(buf, ptr, (unsigned long)chunk, item_size); - array_size += item_size; - ptr += item_size; - btrfs_set_super_sys_array_size(&super, array_size); - - btrfs_set_header_bytenr(buf, cfg->blocks[3]); - btrfs_set_header_owner(buf, BTRFS_CHUNK_TREE_OBJECTID); - btrfs_set_header_nritems(buf, nritems); - csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0); - ret = pwrite(fd, buf->data, cfg->nodesize, cfg->blocks[3]); - if (ret != cfg->nodesize) { - ret = (ret < 0 ? -errno : -EIO); - goto out; - } - - /* create the device tree */ - memset(buf->data + sizeof(struct btrfs_header), 0, - cfg->nodesize - sizeof(struct btrfs_header)); - nritems = 0; - itemoff = __BTRFS_LEAF_DATA_SIZE(cfg->nodesize) - - sizeof(struct btrfs_dev_extent); - - btrfs_set_disk_key_objectid(&disk_key, 1); - btrfs_set_disk_key_offset(&disk_key, 0); - btrfs_set_disk_key_type(&disk_key, BTRFS_DEV_EXTENT_KEY); - btrfs_set_item_key(buf, &disk_key, nritems); - btrfs_set_item_offset(buf, btrfs_item_nr(nritems), itemoff); - btrfs_set_item_size(buf, btrfs_item_nr(nritems), - sizeof(struct btrfs_dev_extent)); - dev_extent = btrfs_item_ptr(buf, nritems, struct btrfs_dev_extent); - btrfs_set_dev_extent_chunk_tree(buf, dev_extent, - BTRFS_CHUNK_TREE_OBJECTID); - btrfs_set_dev_extent_chunk_objectid(buf, dev_extent, - BTRFS_FIRST_CHUNK_TREE_OBJECTID); - btrfs_set_dev_extent_chunk_offset(buf, dev_extent, 0); - - write_extent_buffer(buf, chunk_tree_uuid, - (unsigned long)btrfs_dev_extent_chunk_tree_uuid(dev_extent), - BTRFS_UUID_SIZE); - - btrfs_set_dev_extent_length(buf, dev_extent, - BTRFS_MKFS_SYSTEM_GROUP_SIZE); - nritems++; - - btrfs_set_header_bytenr(buf, cfg->blocks[4]); - btrfs_set_header_owner(buf, BTRFS_DEV_TREE_OBJECTID); - btrfs_set_header_nritems(buf, nritems); - csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0); - ret = pwrite(fd, buf->data, cfg->nodesize, cfg->blocks[4]); - if (ret != cfg->nodesize) { - ret = (ret < 0 ? -errno : -EIO); - goto out; - } - - /* create the FS root */ - memset(buf->data + sizeof(struct btrfs_header), 0, - cfg->nodesize - sizeof(struct btrfs_header)); - btrfs_set_header_bytenr(buf, cfg->blocks[5]); - btrfs_set_header_owner(buf, BTRFS_FS_TREE_OBJECTID); - btrfs_set_header_nritems(buf, 0); - csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0); - ret = pwrite(fd, buf->data, cfg->nodesize, cfg->blocks[5]); - if (ret != cfg->nodesize) { - ret = (ret < 0 ? -errno : -EIO); - goto out; - } - /* finally create the csum root */ - memset(buf->data + sizeof(struct btrfs_header), 0, - cfg->nodesize - sizeof(struct btrfs_header)); - btrfs_set_header_bytenr(buf, cfg->blocks[6]); - btrfs_set_header_owner(buf, BTRFS_CSUM_TREE_OBJECTID); - btrfs_set_header_nritems(buf, 0); - csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0); - ret = pwrite(fd, buf->data, cfg->nodesize, cfg->blocks[6]); - if (ret != cfg->nodesize) { - ret = (ret < 0 ? -errno : -EIO); - goto out; - } - - /* and write out the super block */ - memset(buf->data, 0, BTRFS_SUPER_INFO_SIZE); - memcpy(buf->data, &super, sizeof(super)); - buf->len = BTRFS_SUPER_INFO_SIZE; - csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0); - ret = pwrite(fd, buf->data, BTRFS_SUPER_INFO_SIZE, cfg->blocks[0]); - if (ret != BTRFS_SUPER_INFO_SIZE) { - ret = (ret < 0 ? -errno : -EIO); - goto out; - } - - ret = 0; - -out: - free(buf); - return ret; -} - -static const struct btrfs_fs_feature { - const char *name; - u64 flag; - const char *desc; -} mkfs_features[] = { - { "mixed-bg", BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS, - "mixed data and metadata block groups" }, - { "extref", BTRFS_FEATURE_INCOMPAT_EXTENDED_IREF, - "increased hardlink limit per file to 65536" }, - { "raid56", BTRFS_FEATURE_INCOMPAT_RAID56, - "raid56 extended format" }, - { "skinny-metadata", BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA, - "reduced-size metadata extent refs" }, - { "no-holes", BTRFS_FEATURE_INCOMPAT_NO_HOLES, - "no explicit hole extents for files" }, - /* Keep this one last */ - { "list-all", BTRFS_FEATURE_LIST_ALL, NULL } -}; - -static int parse_one_fs_feature(const char *name, u64 *flags) -{ - int i; - int found = 0; - - for (i = 0; i < ARRAY_SIZE(mkfs_features); i++) { - if (name[0] == '^' && - !strcmp(mkfs_features[i].name, name + 1)) { - *flags &= ~ mkfs_features[i].flag; - found = 1; - } else if (!strcmp(mkfs_features[i].name, name)) { - *flags |= mkfs_features[i].flag; - found = 1; - } - } - - return !found; -} - -void btrfs_parse_features_to_string(char *buf, u64 flags) -{ - int i; - - buf[0] = 0; - - for (i = 0; i < ARRAY_SIZE(mkfs_features); i++) { - if (flags & mkfs_features[i].flag) { - if (*buf) - strcat(buf, ", "); - strcat(buf, mkfs_features[i].name); - } - } -} - -void btrfs_process_fs_features(u64 flags) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(mkfs_features); i++) { - if (flags & mkfs_features[i].flag) { - printf("Turning ON incompat feature '%s': %s\n", - mkfs_features[i].name, - mkfs_features[i].desc); - } - } -} - -void btrfs_list_all_fs_features(u64 mask_disallowed) -{ - int i; - - fprintf(stderr, "Filesystem features available:\n"); - for (i = 0; i < ARRAY_SIZE(mkfs_features) - 1; i++) { - char *is_default = ""; - - if (mkfs_features[i].flag & mask_disallowed) - continue; - if (mkfs_features[i].flag & BTRFS_MKFS_DEFAULT_FEATURES) - is_default = ", default"; - fprintf(stderr, "%-20s- %s (0x%llx%s)\n", - mkfs_features[i].name, - mkfs_features[i].desc, - mkfs_features[i].flag, - is_default); - } -} - -/* - * Return NULL if all features were parsed fine, otherwise return the name of - * the first unparsed. - */ -char* btrfs_parse_fs_features(char *namelist, u64 *flags) -{ - char *this_char; - char *save_ptr = NULL; /* Satisfy static checkers */ - - for (this_char = strtok_r(namelist, ",", &save_ptr); - this_char != NULL; - this_char = strtok_r(NULL, ",", &save_ptr)) { - if (parse_one_fs_feature(this_char, flags)) - return this_char; - } - - return NULL; -} - u64 btrfs_device_size(int fd, struct stat *st) { u64 size; @@ -1586,7 +157,7 @@ static int zero_blocks(int fd, off_t start, size_t len) return ret; } -#define ZERO_DEV_BYTES (2 * 1024 * 1024) +#define ZERO_DEV_BYTES SZ_2M /* don't write outside the device by clamping the region to the device size */ static int zero_dev_clamped(int fd, off_t start, ssize_t len, u64 dev_size) @@ -1606,12 +177,13 @@ static int zero_dev_clamped(int fd, off_t start, ssize_t len, u64 dev_size) } int btrfs_add_to_fsid(struct btrfs_trans_handle *trans, - struct btrfs_root *root, int fd, char *path, + struct btrfs_root *root, int fd, const char *path, u64 device_total_bytes, u32 io_width, u32 io_align, u32 sectorsize) { struct btrfs_super_block *disk_super; - struct btrfs_super_block *super = root->fs_info->super_copy; + struct btrfs_fs_info *fs_info = root->fs_info; + struct btrfs_super_block *super = fs_info->super_copy; struct btrfs_device *device; struct btrfs_dev_item *dev_item; char *buf = NULL; @@ -1646,7 +218,7 @@ int btrfs_add_to_fsid(struct btrfs_trans_handle *trans, device->total_bytes = device_total_bytes; device->bytes_used = 0; device->total_ios = 0; - device->dev_root = root->fs_info->dev_root; + device->dev_root = fs_info->dev_root; device->name = strdup(path); if (!device->name) { ret = -ENOMEM; @@ -1654,7 +226,7 @@ int btrfs_add_to_fsid(struct btrfs_trans_handle *trans, } INIT_LIST_HEAD(&device->dev_list); - ret = btrfs_add_device(trans, root, device); + ret = btrfs_add_device(trans, fs_info, device); if (ret) goto out; @@ -1680,8 +252,8 @@ int btrfs_add_to_fsid(struct btrfs_trans_handle *trans, BUG_ON(ret != sectorsize); free(buf); - list_add(&device->dev_list, &root->fs_info->fs_devices->devices); - device->fs_devices = root->fs_info->fs_devices; + list_add(&device->dev_list, &fs_info->fs_devices->devices); + device->fs_devices = fs_info->fs_devices; return 0; out: @@ -1728,7 +300,7 @@ static int btrfs_wipe_existing_sb(int fd) memset(buf, 0, len); ret = pwrite(fd, buf, len, offset); if (ret < 0) { - error("cannot wipe existing superblock: %s", strerror(errno)); + error("cannot wipe existing superblock: %m"); ret = -1; } else if (ret != len) { error("cannot wipe existing superblock: wrote %d of %zd", ret, len); @@ -1750,7 +322,7 @@ int btrfs_prepare_device(int fd, const char *file, u64 *block_count_ret, ret = fstat(fd, &st); if (ret < 0) { - error("unable to stat %s: %s", file, strerror(errno)); + error("unable to stat %s: %m", file); return 1; } @@ -1770,8 +342,8 @@ int btrfs_prepare_device(int fd, const char *file, u64 *block_count_ret, */ if (discard_range(fd, 0, 0) == 0) { if (opflags & PREP_DEVICE_VERBOSE) - printf("Performing full device TRIM (%s) ...\n", - pretty_size(block_count)); + printf("Performing full device TRIM %s (%s) ...\n", + file, pretty_size(block_count)); discard_blocks(fd, 0, block_count); } } @@ -1810,7 +382,7 @@ int btrfs_make_root_dir(struct btrfs_trans_handle *trans, btrfs_set_stack_inode_generation(&inode_item, trans->transid); btrfs_set_stack_inode_size(&inode_item, 0); btrfs_set_stack_inode_nlink(&inode_item, 1); - btrfs_set_stack_inode_nbytes(&inode_item, root->nodesize); + btrfs_set_stack_inode_nbytes(&inode_item, root->fs_info->nodesize); btrfs_set_stack_inode_mode(&inode_item, S_IFDIR | 0755); btrfs_set_stack_timespec_sec(&inode_item.atime, now); btrfs_set_stack_timespec_nsec(&inode_item.atime, 0); @@ -1818,7 +390,7 @@ int btrfs_make_root_dir(struct btrfs_trans_handle *trans, btrfs_set_stack_timespec_nsec(&inode_item.ctime, 0); btrfs_set_stack_timespec_sec(&inode_item.mtime, now); btrfs_set_stack_timespec_nsec(&inode_item.mtime, 0); - btrfs_set_stack_timespec_sec(&inode_item.otime, 0); + btrfs_set_stack_timespec_sec(&inode_item.otime, now); btrfs_set_stack_timespec_nsec(&inode_item.otime, 0); if (root->fs_info->tree_root == root) @@ -1877,7 +449,7 @@ int is_mount_point(const char *path) return ret; } -static int is_reg_file(const char *path) +int is_reg_file(const char *path) { struct stat statbuf; @@ -1886,6 +458,21 @@ static int is_reg_file(const char *path) return S_ISREG(statbuf.st_mode); } +int is_path_exist(const char *path) +{ + struct stat statbuf; + int ret; + + ret = stat(path, &statbuf); + if (ret < 0) { + if (errno == ENOENT) + return 0; + else + return -errno; + } + return 1; +} + /* * This function checks if the given input parameter is * an uuid or a path @@ -1949,7 +536,7 @@ int get_btrfs_mount(const char *dev, char *mp, size_t mp_size) fd = open(dev, O_RDONLY); if (fd < 0) { ret = -errno; - error("cannot open %s: %s", dev, strerror(errno)); + error("cannot open %s: %m", dev); goto out; } @@ -1987,8 +574,8 @@ int open_path_or_dev_mnt(const char *path, DIR **dirstream, int verbose) return -1; } ret = open_file_or_dir(mp, dirstream); - error_on(verbose && ret < 0, "can't access '%s': %s", - path, strerror(errno)); + error_on(verbose && ret < 0, "can't access '%s': %m", + path); } else { ret = btrfs_open_dir(path, dirstream, 1); } @@ -1999,17 +586,16 @@ int open_path_or_dev_mnt(const char *path, DIR **dirstream, int verbose) /* * Do the following checks before calling open_file_or_dir(): * 1: path is in a btrfs filesystem - * 2: path is a directory + * 2: path is a directory if dir_only is 1 */ -int btrfs_open_dir(const char *path, DIR **dirstream, int verbose) +int btrfs_open(const char *path, DIR **dirstream, int verbose, int dir_only) { struct statfs stfs; struct stat st; int ret; if (statfs(path, &stfs) != 0) { - error_on(verbose, "cannot access '%s': %s", path, - strerror(errno)); + error_on(verbose, "cannot access '%s': %m", path); return -1; } @@ -2019,25 +605,33 @@ int btrfs_open_dir(const char *path, DIR **dirstream, int verbose) } if (stat(path, &st) != 0) { - error_on(verbose, "cannot access '%s': %s", path, - strerror(errno)); + error_on(verbose, "cannot access '%s': %m", path); return -1; } - if (!S_ISDIR(st.st_mode)) { + if (dir_only && !S_ISDIR(st.st_mode)) { error_on(verbose, "not a directory: %s", path); return -3; } ret = open_file_or_dir(path, dirstream); if (ret < 0) { - error_on(verbose, "cannot access '%s': %s", path, - strerror(errno)); + error_on(verbose, "cannot access '%s': %m", path); } return ret; } +int btrfs_open_dir(const char *path, DIR **dirstream, int verbose) +{ + return btrfs_open(path, dirstream, verbose, 1); +} + +int btrfs_open_file_or_dir(const char *path, DIR **dirstream, int verbose) +{ + return btrfs_open(path, dirstream, verbose, 0); +} + /* checks if a device is a loop device */ static int is_loop_device (const char* device) { struct stat statbuf; @@ -2224,14 +818,9 @@ static int blk_file_in_dev_list(struct btrfs_fs_devices* fs_devices, const char* file) { int ret; - struct list_head *head; - struct list_head *cur; struct btrfs_device *device; - head = &fs_devices->devices; - list_for_each(cur, head) { - device = list_entry(cur, struct btrfs_device, dev_list); - + list_for_each_entry(device, &fs_devices->devices, dev_list) { if((ret = is_same_loop_file(device->name, file))) return ret; } @@ -2308,8 +897,7 @@ int check_mounted(const char* file) fd = open(file, O_RDONLY); if (fd < 0) { - error("mount check: cannot open %s: %s", file, - strerror(errno)); + error("mount check: cannot open %s: %m", file); return -errno; } @@ -2336,7 +924,7 @@ int check_mounted_where(int fd, const char *file, char *where, int size, /* scan other devices */ if (is_btrfs && total_devs > 1) { - ret = btrfs_scan_lblkid(); + ret = btrfs_scan_devices(); if (ret) return ret; } @@ -2398,16 +986,14 @@ int btrfs_register_one_device(const char *fname) fd = open("/dev/btrfs-control", O_RDWR); if (fd < 0) { warning( - "failed to open /dev/btrfs-control, skipping device registration: %s", - strerror(errno)); + "failed to open /dev/btrfs-control, skipping device registration: %m"); return -errno; } memset(&args, 0, sizeof(args)); strncpy_null(args.name, fname); ret = ioctl(fd, BTRFS_IOC_SCAN_DEV, &args); if (ret < 0) { - error("device scan failed on '%s': %s", fname, - strerror(errno)); + error("device scan failed on '%s': %m", fname); ret = -errno; } close(fd); @@ -2416,7 +1002,7 @@ int btrfs_register_one_device(const char *fname) /* * Register all devices in the fs_uuid list created in the user - * space. Ensure btrfs_scan_lblkid() is called before this func. + * space. Ensure btrfs_scan_devices() is called before this func. */ int btrfs_register_all_devices(void) { @@ -2507,12 +1093,19 @@ int pretty_size_snprintf(u64 size, char *str, size_t str_size, unsigned unit_mod int mult = 0; const char** suffix = NULL; u64 last_size; + int negative; if (str_size == 0) return 0; + negative = !!(unit_mode & UNITS_NEGATIVE); + unit_mode &= ~UNITS_NEGATIVE; + if ((unit_mode & ~UNITS_MODE_MASK) == UNITS_RAW) { - snprintf(str, str_size, "%llu", size); + if (negative) + snprintf(str, str_size, "%lld", size); + else + snprintf(str, str_size, "%llu", size); return 0; } @@ -2547,10 +1140,22 @@ int pretty_size_snprintf(u64 size, char *str, size_t str_size, unsigned unit_mod num_divs = 0; break; default: - while (size >= mult) { - last_size = size; - size /= mult; - num_divs++; + if (negative) { + s64 ssize = (s64)size; + s64 last_ssize = ssize; + + while ((ssize < 0 ? -ssize : ssize) >= mult) { + last_ssize = ssize; + ssize /= mult; + num_divs++; + } + last_size = (u64)last_ssize; + } else { + while (size >= mult) { + last_size = size; + size /= mult; + num_divs++; + } } /* * If the value is smaller than base, we didn't do any @@ -2568,7 +1173,12 @@ int pretty_size_snprintf(u64 size, char *str, size_t str_size, unsigned unit_mod assert(0); return -1; } - fraction = (float)last_size / base; + + if (negative) { + fraction = (float)(s64)last_size / base; + } else { + fraction = (float)last_size / base; + } return snprintf(str, str_size, "%.2f%s", fraction, suffix[num_divs]); } @@ -2636,6 +1246,7 @@ static int set_label_unmounted(const char *dev, const char *label) return -1; trans = btrfs_start_transaction(root, 1); + BUG_ON(IS_ERR(trans)); __strncpy_null(root->fs_info->super_copy->label, label, BTRFS_LABEL_SIZE - 1); btrfs_commit_transaction(trans, root); @@ -2652,15 +1263,14 @@ static int set_label_mounted(const char *mount_path, const char *labelp) fd = open(mount_path, O_RDONLY | O_NOATIME); if (fd < 0) { - error("unable to access %s: %s", mount_path, strerror(errno)); + error("unable to access %s: %m", mount_path); return -1; } memset(label, 0, sizeof(label)); __strncpy_null(label, labelp, BTRFS_LABEL_SIZE - 1); if (ioctl(fd, BTRFS_IOC_SET_FSLABEL, label) < 0) { - error("unable to set label of %s: %s", mount_path, - strerror(errno)); + error("unable to set label of %s: %m", mount_path); close(fd); return -1; } @@ -2708,7 +1318,7 @@ int get_label_mounted(const char *mount_path, char *labelp) fd = open(mount_path, O_RDONLY | O_NOATIME); if (fd < 0) { - error("unable to access %s: %s", mount_path, strerror(errno)); + error("unable to access %s: %m", mount_path); return -1; } @@ -2716,8 +1326,7 @@ int get_label_mounted(const char *mount_path, char *labelp) ret = ioctl(fd, BTRFS_IOC_GET_FSLABEL, label); if (ret < 0) { if (errno != ENOTTY) - error("unable to get label of %s: %s", mount_path, - strerror(errno)); + error("unable to get label of %s: %m", mount_path); ret = -errno; close(fd); return ret; @@ -2846,6 +1455,7 @@ u64 parse_qgroupid(const char *p) char *s = strchr(p, '/'); const char *ptr_src_end = p + strlen(p); char *ptr_parse_end = NULL; + enum btrfs_util_error err; u64 level; u64 id; int fd; @@ -2873,13 +1483,13 @@ u64 parse_qgroupid(const char *p) path: /* Path format like subv at 'my_subvol' is the fallback case */ - ret = test_issubvolume(p); - if (ret < 0 || !ret) + err = btrfs_util_is_subvolume(p); + if (err) goto err; fd = open(p, O_RDONLY); if (fd < 0) goto err; - ret = lookup_ino_rootid(fd, &id); + ret = lookup_path_rootid(fd, &id); if (ret) error("failed to lookup root id: %s", strerror(-ret)); close(fd); @@ -2934,10 +1544,16 @@ int open_file_or_dir(const char *fname, DIR **dirstream) void close_file_or_dir(int fd, DIR *dirstream) { - if (dirstream) + int old_errno; + + old_errno = errno; + if (dirstream) { closedir(dirstream); - else if (fd >= 0) + } else if (fd >= 0) { close(fd); + } + + errno = old_errno; } int get_device_info(int fd, u64 devid, @@ -3031,13 +1647,13 @@ again: * * Returns 0 on success, or a negative errno. */ -int get_fs_info(char *path, struct btrfs_ioctl_fs_info_args *fi_args, +int get_fs_info(const char *path, struct btrfs_ioctl_fs_info_args *fi_args, struct btrfs_ioctl_dev_info_args **di_ret) { int fd = -1; int ret = 0; int ndevs = 0; - int i = 0; + u64 last_devid = 0; int replacing = 0; struct btrfs_fs_devices *fs_devices_mnt = NULL; struct btrfs_ioctl_dev_info_args *di_args; @@ -3050,13 +1666,12 @@ int get_fs_info(char *path, struct btrfs_ioctl_fs_info_args *fi_args, if (is_block_device(path) == 1) { struct btrfs_super_block *disk_super; char buf[BTRFS_SUPER_INFO_SIZE]; - u64 devid; /* Ensure it's mounted, then set path to the mountpoint */ fd = open(path, O_RDONLY); if (fd < 0) { ret = -errno; - error("cannot open %s: %s", path, strerror(errno)); + error("cannot open %s: %m", path); goto out; } ret = check_mounted_where(fd, path, mp, sizeof(mp), @@ -3078,10 +1693,8 @@ int get_fs_info(char *path, struct btrfs_ioctl_fs_info_args *fi_args, ret = -EIO; goto out; } - devid = btrfs_stack_device_id(&disk_super->dev_item); - - fi_args->max_id = devid; - i = devid; + last_devid = btrfs_stack_device_id(&disk_super->dev_item); + fi_args->max_id = last_devid; memcpy(fi_args->fsid, fs_devices_mnt->fsid, BTRFS_FSID_SIZE); close(fd); @@ -3118,8 +1731,8 @@ int get_fs_info(char *path, struct btrfs_ioctl_fs_info_args *fi_args, fi_args->num_devices++; ndevs++; replacing = 1; - if (i == 0) - i++; + if (last_devid == 0) + last_devid++; } } @@ -3134,8 +1747,8 @@ int get_fs_info(char *path, struct btrfs_ioctl_fs_info_args *fi_args, if (replacing) memcpy(di_args, &tmp, sizeof(tmp)); - for (; i <= fi_args->max_id; ++i) { - ret = get_device_info(fd, i, &di_args[ndevs]); + for (; last_devid <= fi_args->max_id; last_devid++) { + ret = get_device_info(fd, last_devid, &di_args[ndevs]); if (ret == -ENODEV) continue; if (ret) @@ -3157,157 +1770,107 @@ out: return ret; } -#define isoctal(c) (((c) & ~7) == '0') - -static inline void translate(char *f, char *t) -{ - while (*f != '\0') { - if (*f == '\\' && - isoctal(f[1]) && isoctal(f[2]) && isoctal(f[3])) { - *t++ = 64*(f[1] & 7) + 8*(f[2] & 7) + (f[3] & 7); - f += 4; - } else - *t++ = *f++; - } - *t = '\0'; - return; -} - -/* - * Checks if the swap device. - * Returns 1 if swap device, < 0 on error or 0 if not swap device. - */ -static int is_swap_device(const char *file) +int get_fsid(const char *path, u8 *fsid, int silent) { - FILE *f; - struct stat st_buf; - dev_t dev; - ino_t ino = 0; - char tmp[PATH_MAX]; - char buf[PATH_MAX]; - char *cp; - int ret = 0; - - if (stat(file, &st_buf) < 0) - return -errno; - if (S_ISBLK(st_buf.st_mode)) - dev = st_buf.st_rdev; - else if (S_ISREG(st_buf.st_mode)) { - dev = st_buf.st_dev; - ino = st_buf.st_ino; - } else - return 0; - - if ((f = fopen("/proc/swaps", "r")) == NULL) - return 0; + int ret; + int fd; + struct btrfs_ioctl_fs_info_args args; - /* skip the first line */ - if (fgets(tmp, sizeof(tmp), f) == NULL) + fd = open(path, O_RDONLY); + if (fd < 0) { + ret = -errno; + if (!silent) + error("failed to open %s: %s", path, + strerror(-ret)); goto out; + } - while (fgets(tmp, sizeof(tmp), f) != NULL) { - if ((cp = strchr(tmp, ' ')) != NULL) - *cp = '\0'; - if ((cp = strchr(tmp, '\t')) != NULL) - *cp = '\0'; - translate(tmp, buf); - if (stat(buf, &st_buf) != 0) - continue; - if (S_ISBLK(st_buf.st_mode)) { - if (dev == st_buf.st_rdev) { - ret = 1; - break; - } - } else if (S_ISREG(st_buf.st_mode)) { - if (dev == st_buf.st_dev && ino == st_buf.st_ino) { - ret = 1; - break; - } - } + ret = ioctl(fd, BTRFS_IOC_FS_INFO, &args); + if (ret < 0) { + ret = -errno; + goto out; } -out: - fclose(f); + memcpy(fsid, args.fsid, BTRFS_FSID_SIZE); + ret = 0; +out: + if (fd != -1) + close(fd); return ret; } -/* - * Check for existing filesystem or partition table on device. - * Returns: - * 1 for existing fs or partition - * 0 for nothing found - * -1 for internal error - */ -static int check_overwrite(const char *device) +int is_seen_fsid(u8 *fsid, struct seen_fsid *seen_fsid_hash[]) { - const char *type; - blkid_probe pr = NULL; - int ret; - blkid_loff_t size; + u8 hash = fsid[0]; + int slot = hash % SEEN_FSID_HASH_SIZE; + struct seen_fsid *seen = seen_fsid_hash[slot]; - if (!device || !*device) - return 0; + while (seen) { + if (memcmp(seen->fsid, fsid, BTRFS_FSID_SIZE) == 0) + return 1; - ret = -1; /* will reset on success of all setup calls */ + seen = seen->next; + } - pr = blkid_new_probe_from_filename(device); - if (!pr) - goto out; + return 0; +} - size = blkid_probe_get_size(pr); - if (size < 0) - goto out; +int add_seen_fsid(u8 *fsid, struct seen_fsid *seen_fsid_hash[], + int fd, DIR *dirstream) +{ + u8 hash = fsid[0]; + int slot = hash % SEEN_FSID_HASH_SIZE; + struct seen_fsid *seen = seen_fsid_hash[slot]; + struct seen_fsid *alloc; - /* nothing to overwrite on a 0-length device */ - if (size == 0) { - ret = 0; - goto out; - } + if (!seen) + goto insert; - ret = blkid_probe_enable_partitions(pr, 1); - if (ret < 0) - goto out; + while (1) { + if (memcmp(seen->fsid, fsid, BTRFS_FSID_SIZE) == 0) + return -EEXIST; - ret = blkid_do_fullprobe(pr); - if (ret < 0) - goto out; + if (!seen->next) + break; - /* - * Blkid returns 1 for nothing found and 0 when it finds a signature, - * but we want the exact opposite, so reverse the return value here. - * - * In addition print some useful diagnostics about what actually is - * on the device. - */ - if (ret) { - ret = 0; - goto out; + seen = seen->next; } - if (!blkid_probe_lookup_value(pr, "TYPE", &type, NULL)) { - fprintf(stderr, - "%s appears to contain an existing " - "filesystem (%s).\n", device, type); - } else if (!blkid_probe_lookup_value(pr, "PTTYPE", &type, NULL)) { - fprintf(stderr, - "%s appears to contain a partition " - "table (%s).\n", device, type); - } else { - fprintf(stderr, - "%s appears to contain something weird " - "according to blkid\n", device); - } - ret = 1; +insert: + alloc = malloc(sizeof(*alloc)); + if (!alloc) + return -ENOMEM; -out: - if (pr) - blkid_free_probe(pr); - if (ret == -1) - fprintf(stderr, - "probe of %s failed, cannot detect " - "existing filesystem.\n", device); - return ret; + alloc->next = NULL; + memcpy(alloc->fsid, fsid, BTRFS_FSID_SIZE); + alloc->fd = fd; + alloc->dirstream = dirstream; + + if (seen) + seen->next = alloc; + else + seen_fsid_hash[slot] = alloc; + + return 0; +} + +void free_seen_fsid(struct seen_fsid *seen_fsid_hash[]) +{ + int slot; + struct seen_fsid *seen; + struct seen_fsid *next; + + for (slot = 0; slot < SEEN_FSID_HASH_SIZE; slot++) { + seen = seen_fsid_hash[slot]; + while (seen) { + next = seen->next; + close_file_or_dir(seen->fd, seen->dirstream); + free(seen); + seen = next; + } + seen_fsid_hash[slot] = NULL; + } } static int group_profile_devs_min(u64 flag) @@ -3398,64 +1961,7 @@ int group_profile_max_safe_loss(u64 flags) } } -/* - * Check if a device is suitable for btrfs - * returns: - * 1: something is wrong, an error is printed - * 0: all is fine - */ -int test_dev_for_mkfs(const char *file, int force_overwrite) -{ - int ret, fd; - struct stat st; - - ret = is_swap_device(file); - if (ret < 0) { - error("checking status of %s: %s", file, strerror(-ret)); - return 1; - } - if (ret == 1) { - error("%s is a swap device", file); - return 1; - } - if (!force_overwrite) { - if (check_overwrite(file)) { - error("use the -f option to force overwrite of %s", - file); - return 1; - } - } - ret = check_mounted(file); - if (ret < 0) { - error("cannot check mount status of %s: %s", file, - strerror(-ret)); - return 1; - } - if (ret == 1) { - error("%s is mounted", file); - return 1; - } - /* check if the device is busy */ - fd = open(file, O_RDWR|O_EXCL); - if (fd < 0) { - error("unable to open %s: %s", file, strerror(errno)); - return 1; - } - if (fstat(fd, &st)) { - error("unable to stat %s: %s", file, strerror(errno)); - close(fd); - return 1; - } - if (!S_ISBLK(st.st_mode)) { - error("%s is not a block device", file); - close(fd); - return 1; - } - close(fd); - return 0; -} - -int btrfs_scan_lblkid(void) +int btrfs_scan_devices(void) { int fd = -1; int ret; @@ -3485,7 +1991,7 @@ int btrfs_scan_lblkid(void) fd = open(path, O_RDONLY); if (fd < 0) { - error("cannot open %s: %s", path, strerror(errno)); + error("cannot open %s: %m", path); continue; } ret = btrfs_scan_one_device(fd, path, &tmp_devices, @@ -3507,35 +2013,6 @@ int btrfs_scan_lblkid(void) return 0; } -int is_vol_small(const char *file) -{ - int fd = -1; - int e; - struct stat st; - u64 size; - - fd = open(file, O_RDONLY); - if (fd < 0) - return -errno; - if (fstat(fd, &st) < 0) { - e = -errno; - close(fd); - return e; - } - size = btrfs_device_size(fd, &st); - if (size == 0) { - close(fd); - return -1; - } - if (size < BTRFS_MKFS_SMALL_VOLUME_SIZE) { - close(fd); - return 1; - } else { - close(fd); - return 0; - } -} - /* * This reads a line from the stdin and only returns non-zero if the * first whitespace delimited token is a case insensitive match with yes @@ -3555,31 +2032,6 @@ int ask_user(const char *question) } /* - * For a given: - * - file or directory return the containing tree root id - * - subvolume return its own tree id - * - BTRFS_EMPTY_SUBVOL_DIR_OBJECTID (directory with ino == 2) the result is - * undefined and function returns -1 - */ -int lookup_ino_rootid(int fd, u64 *rootid) -{ - struct btrfs_ioctl_ino_lookup_args args; - int ret; - - memset(&args, 0, sizeof(args)); - args.treeid = 0; - args.objectid = BTRFS_FIRST_FREE_OBJECTID; - - ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args); - if (ret < 0) - return -errno; - - *rootid = args.treeid; - - return 0; -} - -/* * return 0 if a btrfs mount point is found * return 1 if a mount point is found but not btrfs * return <0 if something goes wrong @@ -3634,27 +2086,6 @@ int find_mount_root(const char *path, char **mount_root) return ret; } -int test_minimum_size(const char *file, u32 nodesize) -{ - int fd; - struct stat statbuf; - - fd = open(file, O_RDONLY); - if (fd < 0) - return -errno; - if (stat(file, &statbuf) < 0) { - close(fd); - return -errno; - } - if (btrfs_device_size(fd, &statbuf) < btrfs_min_dev_size(nodesize)) { - close(fd); - return 1; - } - close(fd); - return 0; -} - - /* * Test if path is a directory * Returns: @@ -3778,6 +2209,10 @@ u64 get_partition_size(const char *dev) return result; } +/* + * Check if the BTRFS_IOC_TREE_SEARCH_V2 ioctl is supported on a given + * filesystem, opened at fd + */ int btrfs_tree_search2_ioctl_supported(int fd) { struct btrfs_ioctl_search_args_v2 *args2; @@ -3785,10 +2220,6 @@ int btrfs_tree_search2_ioctl_supported(int fd) int args2_size = 1024; char args2_buf[args2_size]; int ret; - static int v2_supported = -1; - - if (v2_supported != -1) - return v2_supported; args2 = (struct btrfs_ioctl_search_args_v2 *)args2_buf; sk = &(args2->key); @@ -3809,13 +2240,10 @@ int btrfs_tree_search2_ioctl_supported(int fd) args2->buf_size = args2_size - sizeof(struct btrfs_ioctl_search_args_v2); ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH_V2, args2); if (ret == -EOPNOTSUPP) - v2_supported = 0; + return 0; else if (ret == 0) - v2_supported = 1; - else - return ret; - - return v2_supported; + return 1; + return ret; } int btrfs_check_nodesize(u32 nodesize, u32 sectorsize, u64 features) @@ -3965,8 +2393,36 @@ unsigned int get_unit_mode_from_arg(int *argc, char *argv[], int df_mode) return unit_mode; } +u64 div_factor(u64 num, int factor) +{ + if (factor == 10) + return num; + num *= factor; + num /= 10; + return num; +} +/* + * Get the length of the string converted from a u64 number. + * + * Result is equal to log10(num) + 1, but without the use of math library. + */ +int count_digits(u64 num) +{ + int ret = 0; + + if (num == 0) + return 1; + while (num > 0) { + ret++; + num /= 10; + } + return ret; +} + int string_is_numerical(const char *str) { + if (!str) + return 0; if (!(*str >= '0' && *str <= '9')) return 0; while (*str >= '0' && *str <= '9') @@ -3976,48 +2432,13 @@ int string_is_numerical(const char *str) return 1; } -/* - * Preprocess @argv with getopt_long to reorder options and consume the "--" - * option separator. - * Unknown short and long options are reported, optionally the @usage is printed - * before exit. - */ -void clean_args_no_options(int argc, char *argv[], const char * const *usagestr) -{ - static const struct option long_options[] = { - {NULL, 0, NULL, 0} - }; - - while (1) { - int c = getopt_long(argc, argv, "", long_options, NULL); - - if (c < 0) - break; - - switch (c) { - default: - if (usagestr) - usage(usagestr); - } - } -} - -/* - * Same as clean_args_no_options but pass through arguments that could look - * like short options. Eg. reisze which takes a negative resize argument like - * '-123M' . - * - * This accepts only two forms: - * - "-- option1 option2 ..." - * - "option1 option2 ..." - */ -void clean_args_no_options_relaxed(int argc, char *argv[], const char * const *usagestr) +int prefixcmp(const char *str, const char *prefix) { - if (argc <= 1) - return; - - if (strcmp(argv[1], "--") == 0) - optind = 2; + for (; ; str++, prefix++) + if (!*prefix) + return 0; + else if (*str != *prefix) + return (unsigned char)*prefix - (unsigned char)*str; } /* Subvolume helper functions */ @@ -4033,118 +2454,24 @@ int test_issubvolname(const char *name) strcmp(name, ".") && strcmp(name, ".."); } -/* - * Test if path is a subvolume - * Returns: - * 0 - path exists but it is not a subvolume - * 1 - path exists and it is a subvolume - * < 0 - error - */ -int test_issubvolume(const char *path) -{ - struct stat st; - struct statfs stfs; - int res; - - res = stat(path, &st); - if (res < 0) - return -errno; - - if (st.st_ino != BTRFS_FIRST_FREE_OBJECTID || !S_ISDIR(st.st_mode)) - return 0; - - res = statfs(path, &stfs); - if (res < 0) - return -errno; - - return (int)stfs.f_type == BTRFS_SUPER_MAGIC; -} - const char *subvol_strip_mountpoint(const char *mnt, const char *full_path) { int len = strlen(mnt); if (!len) return full_path; + if ((strncmp(mnt, full_path, len) != 0) || (full_path[len] != '/')) { + error("not on mount point: %s", mnt); + exit(1); + } + if (mnt[len - 1] != '/') len += 1; return full_path + len; } -/* - * Returns - * <0: Std error - * 0: All fine - * 1: Error; and error info printed to the terminal. Fixme. - * 2: If the fullpath is root tree instead of subvol tree - */ -int get_subvol_info(const char *fullpath, struct root_info *get_ri) -{ - u64 sv_id; - int ret = 1; - int fd = -1; - int mntfd = -1; - char *mnt = NULL; - const char *svpath = NULL; - DIR *dirstream1 = NULL; - DIR *dirstream2 = NULL; - - ret = test_issubvolume(fullpath); - if (ret < 0) - return ret; - if (!ret) { - error("not a subvolume: %s", fullpath); - return 1; - } - - ret = find_mount_root(fullpath, &mnt); - if (ret < 0) - return ret; - if (ret > 0) { - error("%s doesn't belong to btrfs mount point", fullpath); - return 1; - } - ret = 1; - svpath = subvol_strip_mountpoint(mnt, fullpath); - - fd = btrfs_open_dir(fullpath, &dirstream1, 1); - if (fd < 0) - goto out; - - ret = btrfs_list_get_path_rootid(fd, &sv_id); - if (ret) { - error("can't get rootid for '%s'", fullpath); - goto out; - } - - mntfd = btrfs_open_dir(mnt, &dirstream2, 1); - if (mntfd < 0) - goto out; - - if (sv_id == BTRFS_FS_TREE_OBJECTID) { - ret = 2; - /* - * So that caller may decide if thats an error or just fine. - */ - goto out; - } - - memset(get_ri, 0, sizeof(*get_ri)); - get_ri->root_id = sv_id; - - ret = btrfs_get_subvol(mntfd, get_ri); - if (ret) - error("can't find '%s': %d", svpath, ret); - -out: - close_file_or_dir(mntfd, dirstream2); - close_file_or_dir(fd, dirstream1); - free(mnt); - - return ret; -} - +/* Set the seed manually */ void init_rand_seed(u64 seed) { int i; @@ -4194,6 +2521,7 @@ u32 rand_u32(void) return (u32)jrand48(rand_seed); } +/* Return random number in range [0, upper) */ unsigned int rand_range(unsigned int upper) { __init_seed(); @@ -4203,3 +2531,62 @@ unsigned int rand_range(unsigned int upper) */ return (unsigned int)(jrand48(rand_seed) % upper); } + +int rand_int(void) +{ + return (int)(rand_u32()); +} + +u64 rand_u64(void) +{ + u64 ret = 0; + + ret += rand_u32(); + ret <<= 32; + ret += rand_u32(); + return ret; +} + +u16 rand_u16(void) +{ + return (u16)(rand_u32()); +} + +u8 rand_u8(void) +{ + return (u8)(rand_u32()); +} + +void btrfs_config_init(void) +{ +} + +/* Returns total size of main memory in bytes, -1UL if error. */ +unsigned long total_memory(void) +{ + struct sysinfo si; + + if (sysinfo(&si) < 0) { + error("can't determine memory size"); + return -1UL; + } + return si.totalram * si.mem_unit; /* bytes */ +} + +void print_device_info(struct btrfs_device *device, char *prefix) +{ + if (prefix) + printf("%s", prefix); + printf("Device: id = %llu, name = %s\n", + device->devid, device->name); +} + +void print_all_devices(struct list_head *devices) +{ + struct btrfs_device *dev; + + printf("All Devices:\n"); + list_for_each_entry(dev, devices, dev_list) + print_device_info(dev, "\t"); + printf("\n"); +}