X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=utils.c;h=715bab0ebfb56e91b92338385bd5838add274235;hb=660adc0e1cad9fa46d4f689ae9c5c3202041863f;hp=06efcabef95afaf323396936d3998aa4504fb7fe;hpb=37288902fca5a3662620de544eca9dd3b6d6964b;p=platform%2Fupstream%2Fbtrfs-progs.git diff --git a/utils.c b/utils.c index 06efcab..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,57 +60,10 @@ static int btrfs_scan_done = 0; -static char argv0_buf[ARGV0_BUF_SIZE] = "btrfs"; - -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; -} +static int rand_seed_initlized = 0; +static unsigned short rand_seed[3]; +struct btrfs_config bconf; /* * Discard the given range in one go @@ -127,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); @@ -140,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; @@ -178,504 +126,6 @@ int test_uuid_unique(char *fs_uuid) return unique; } -/* - * @fs_uuid - if NULL, generates a UUID, returns back the new filesystem UUID - */ -int make_btrfs(int fd, struct btrfs_mkfs_config *cfg) -{ - 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; - - 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 && *cfg->fs_uuid) { - if (uuid_parse(cfg->fs_uuid, super.fsid) != 0) { - fprintf(stderr, "could not parse UUID: %s\n", - cfg->fs_uuid); - ret = -EINVAL; - goto out; - } - if (!test_uuid_unique(cfg->fs_uuid)) { - fprintf(stderr, "non-unique UUID: %s\n", 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); - 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(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); - - BUG_ON(cfg->blocks[i] < first_free); - BUG_ON(cfg->blocks[i] < cfg->blocks[i - 1]); - - /* 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 */ - BUG_ON(sizeof(super) > cfg->sectorsize); - 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; @@ -707,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) @@ -727,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; @@ -742,13 +193,16 @@ int btrfs_add_to_fsid(struct btrfs_trans_handle *trans, device_total_bytes = (device_total_bytes / sectorsize) * sectorsize; - device = kzalloc(sizeof(*device), GFP_NOFS); - if (!device) - goto err_nomem; - buf = kzalloc(sectorsize, GFP_NOFS); - if (!buf) - goto err_nomem; - BUG_ON(sizeof(*disk_super) > sectorsize); + device = calloc(1, sizeof(*device)); + if (!device) { + ret = -ENOMEM; + goto out; + } + buf = calloc(1, sectorsize); + if (!buf) { + ret = -ENOMEM; + goto out; + } disk_super = (struct btrfs_super_block *)buf; dev_item = &disk_super->dev_item; @@ -764,14 +218,17 @@ 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) - goto err_nomem; + if (!device->name) { + ret = -ENOMEM; + goto out; + } INIT_LIST_HEAD(&device->dev_list); - ret = btrfs_add_device(trans, root, device); - BUG_ON(ret); + ret = btrfs_add_device(trans, fs_info, device); + if (ret) + goto out; fs_total_bytes = btrfs_super_total_bytes(super) + device_total_bytes; btrfs_set_super_total_bytes(super, fs_total_bytes); @@ -794,15 +251,15 @@ int btrfs_add_to_fsid(struct btrfs_trans_handle *trans, ret = pwrite(fd, buf, sectorsize, BTRFS_SUPER_INFO_OFFSET); BUG_ON(ret != sectorsize); - kfree(buf); - list_add(&device->dev_list, &root->fs_info->fs_devices->devices); - device->fs_devices = root->fs_info->fs_devices; + free(buf); + list_add(&device->dev_list, &fs_info->fs_devices->devices); + device->fs_devices = fs_info->fs_devices; return 0; -err_nomem: - kfree(device); - kfree(buf); - return -ENOMEM; +out: + free(device); + free(buf); + return ret; } static int btrfs_wipe_existing_sb(int fd) @@ -843,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); @@ -856,8 +313,8 @@ out: return ret; } -int btrfs_prepare_device(int fd, char *file, int zero_end, u64 *block_count_ret, - u64 max_block_count, int discard) +int btrfs_prepare_device(int fd, const char *file, u64 *block_count_ret, + u64 max_block_count, unsigned opflags) { u64 block_count; struct stat st; @@ -865,7 +322,7 @@ int btrfs_prepare_device(int fd, char *file, int zero_end, 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; } @@ -877,15 +334,16 @@ int btrfs_prepare_device(int fd, char *file, int zero_end, u64 *block_count_ret, if (max_block_count) block_count = min(block_count, max_block_count); - if (discard) { + if (opflags & PREP_DEVICE_DISCARD) { /* * We intentionally ignore errors from the discard ioctl. It * is not necessary for the mkfs functionality but just an * optimization. */ if (discard_range(fd, 0, 0) == 0) { - printf("Performing full device TRIM (%s) ...\n", - pretty_size(block_count)); + if (opflags & PREP_DEVICE_VERBOSE) + printf("Performing full device TRIM %s (%s) ...\n", + file, pretty_size(block_count)); discard_blocks(fd, 0, block_count); } } @@ -894,7 +352,7 @@ int btrfs_prepare_device(int fd, char *file, int zero_end, u64 *block_count_ret, for (i = 0 ; !ret && i < BTRFS_SUPER_MIRROR_MAX; i++) ret = zero_dev_clamped(fd, btrfs_sb_offset(i), BTRFS_SUPER_INFO_SIZE, block_count); - if (!ret && zero_end) + if (!ret && (opflags & PREP_DEVICE_ZERO_END)) ret = zero_dev_clamped(fd, block_count - ZERO_DEV_BYTES, ZERO_DEV_BYTES, block_count); @@ -924,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); @@ -932,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) @@ -991,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; @@ -1000,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 @@ -1063,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; } @@ -1101,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); } @@ -1113,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; } @@ -1133,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; @@ -1338,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; } @@ -1422,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; } @@ -1445,17 +919,17 @@ int check_mounted_where(int fd, const char *file, char *where, int size, /* scan the initial device */ ret = btrfs_scan_one_device(fd, file, &fs_devices_mnt, - &total_devs, BTRFS_SUPER_INFO_OFFSET, 0); + &total_devs, BTRFS_SUPER_INFO_OFFSET, SBREAD_DEFAULT); is_btrfs = (ret >= 0); /* scan other devices */ if (is_btrfs && total_devs > 1) { - ret = btrfs_scan_lblkid(); + ret = btrfs_scan_devices(); if (ret) return ret; } - /* iterate over the list of currently mountes filesystems */ + /* iterate over the list of currently mounted filesystems */ if ((f = setmntent ("/proc/self/mounts", "r")) == NULL) return -errno; @@ -1512,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); @@ -1530,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) { @@ -1573,7 +1045,12 @@ int btrfs_device_already_in_root(struct btrfs_root *root, int fd, ret = 0; disk_super = (struct btrfs_super_block *)buf; - if (btrfs_super_magic(disk_super) != BTRFS_MAGIC) + /* + * Accept devices from the same filesystem, allow partially created + * structures. + */ + if (btrfs_super_magic(disk_super) != BTRFS_MAGIC && + btrfs_super_magic(disk_super) != BTRFS_MAGIC_PARTIAL) goto brelse; if (!memcmp(disk_super->fsid, root->fs_info->super_copy->fsid, @@ -1616,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; } @@ -1656,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 @@ -1677,13 +1173,18 @@ 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]); } /* - * __strncpy__null - strncpy with null termination + * __strncpy_null - strncpy with null termination * @dest: the target array * @src: the source string * @n: maximum bytes to copy (size of *dest) @@ -1694,7 +1195,7 @@ int pretty_size_snprintf(u64 size, char *str, size_t str_size, unsigned unit_mod * byte ('\0'), to the buffer pointed to by dest, up to a maximum * of n bytes. Then ensure that dest is null-terminated. */ -char *__strncpy__null(char *dest, const char *src, size_t n) +char *__strncpy_null(char *dest, const char *src, size_t n) { strncpy(dest, src, n); if (n > 0) @@ -1713,8 +1214,8 @@ static int check_label(const char *input) int len = strlen(input); if (len > BTRFS_LABEL_SIZE - 1) { - fprintf(stderr, "ERROR: Label %s is too long (max %d)\n", - input, BTRFS_LABEL_SIZE - 1); + error("label %s is too long (max %d)", input, + BTRFS_LABEL_SIZE - 1); return -1; } @@ -1729,12 +1230,11 @@ static int set_label_unmounted(const char *dev, const char *label) ret = check_mounted(dev); if (ret < 0) { - fprintf(stderr, "FATAL: error checking %s mount status\n", dev); + error("checking mount status of %s failed: %d", dev, ret); return -1; } if (ret > 0) { - fprintf(stderr, "ERROR: dev %s is mounted, use mount point\n", - dev); + error("device %s is mounted, use mount point", dev); return -1; } @@ -1746,8 +1246,9 @@ static int set_label_unmounted(const char *dev, const char *label) return -1; trans = btrfs_start_transaction(root, 1); - snprintf(root->fs_info->super_copy->label, BTRFS_LABEL_SIZE, "%s", - label); + BUG_ON(IS_ERR(trans)); + __strncpy_null(root->fs_info->super_copy->label, label, BTRFS_LABEL_SIZE - 1); + btrfs_commit_transaction(trans, root); /* Now we close it since we are done. */ @@ -1762,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) { - fprintf(stderr, "ERROR: unable to access '%s'\n", mount_path); + error("unable to access %s: %m", mount_path); return -1; } memset(label, 0, sizeof(label)); - strncpy(label, labelp, sizeof(label)); + __strncpy_null(label, labelp, BTRFS_LABEL_SIZE - 1); if (ioctl(fd, BTRFS_IOC_SET_FSLABEL, label) < 0) { - fprintf(stderr, "ERROR: unable to set label %s\n", - strerror(errno)); + error("unable to set label of %s: %m", mount_path); close(fd); return -1; } @@ -1786,7 +1286,7 @@ int get_label_unmounted(const char *dev, char *label) ret = check_mounted(dev); if (ret < 0) { - fprintf(stderr, "FATAL: error checking %s mount status\n", dev); + error("checking mount status of %s failed: %d", dev, ret); return -1; } @@ -1797,7 +1297,8 @@ int get_label_unmounted(const char *dev, char *label) if(!root) return -1; - memcpy(label, root->fs_info->super_copy->label, BTRFS_LABEL_SIZE); + __strncpy_null(label, root->fs_info->super_copy->label, + BTRFS_LABEL_SIZE - 1); /* Now we close it since we are done. */ close_ctree(root); @@ -1817,7 +1318,7 @@ int get_label_mounted(const char *mount_path, char *labelp) fd = open(mount_path, O_RDONLY | O_NOATIME); if (fd < 0) { - fprintf(stderr, "ERROR: unable to access '%s'\n", mount_path); + error("unable to access %s: %m", mount_path); return -1; } @@ -1825,14 +1326,13 @@ int get_label_mounted(const char *mount_path, char *labelp) ret = ioctl(fd, BTRFS_IOC_GET_FSLABEL, label); if (ret < 0) { if (errno != ENOTTY) - fprintf(stderr, "ERROR: unable to get label %s\n", - strerror(errno)); + error("unable to get label of %s: %m", mount_path); ret = -errno; close(fd); return ret; } - strncpy(labelp, label, sizeof(label)); + __strncpy_null(labelp, label, BTRFS_LABEL_SIZE - 1); close(fd); return 0; } @@ -1888,21 +1388,20 @@ u64 parse_size(char *s) u64 ret; if (!s) { - fprintf(stderr, "ERROR: Size value is empty\n"); + error("size value is empty"); exit(1); } if (s[0] == '-') { - fprintf(stderr, - "ERROR: Size value '%s' is less equal than 0\n", s); + error("size value '%s' is less equal than 0", s); exit(1); } ret = strtoull(s, &endptr, 10); if (endptr == s) { - fprintf(stderr, "ERROR: Size value '%s' is invalid\n", s); + error("size value '%s' is invalid", s); exit(1); } if (endptr[0] && endptr[1]) { - fprintf(stderr, "ERROR: Illegal suffix contains character '%c' in wrong position\n", + error("illegal suffix contains character '%c' in wrong position", endptr[1]); exit(1); } @@ -1911,8 +1410,7 @@ u64 parse_size(char *s) * need to call strtoull to get the real size */ if (errno == ERANGE && ret == ULLONG_MAX) { - fprintf(stderr, - "ERROR: Size value '%s' is too large for u64\n", s); + error("size value '%s' is too large for u64", s); exit(1); } if (endptr[0]) { @@ -1939,15 +1437,13 @@ u64 parse_size(char *s) case 'b': break; default: - fprintf(stderr, "ERROR: Unknown size descriptor '%c'\n", - c); + error("unknown size descriptor '%c'", c); exit(1); } } /* Check whether ret * mult overflow */ if (fls64(ret) + fls64(mult) - 1 > 64) { - fprintf(stderr, - "ERROR: Size value '%s' is too large for u64\n", s); + error("size value '%s' is too large for u64", s); exit(1); } ret *= mult; @@ -1959,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; @@ -1986,20 +1483,22 @@ 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); if (ret < 0) goto err; return id; err: - fprintf(stderr, "ERROR: invalid qgroupid or subvolume path: %s\n", p); + error("invalid qgroupid or subvolume path: %s", p); exit(-1); } @@ -2045,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, @@ -2142,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; @@ -2161,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), @@ -2189,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); @@ -2229,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++; } } @@ -2245,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) @@ -2268,159 +1770,107 @@ out: return ret; } -#define isoctal(c) (((c) & ~7) == '0') - -static inline void translate(char *f, char *t) +int get_fsid(const char *path, u8 *fsid, int silent) { - 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) -{ - 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( - 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) @@ -2446,6 +1896,7 @@ int test_num_disk_vs_raid(u64 metadata_profile, u64 data_profile, u64 dev_cnt, int mixed, int ssd) { u64 allowed = 0; + u64 profile = metadata_profile | data_profile; switch (dev_cnt) { default: @@ -2456,16 +1907,12 @@ int test_num_disk_vs_raid(u64 metadata_profile, u64 data_profile, case 2: allowed |= BTRFS_BLOCK_GROUP_RAID0 | BTRFS_BLOCK_GROUP_RAID1 | BTRFS_BLOCK_GROUP_RAID5; - break; case 1: allowed |= BTRFS_BLOCK_GROUP_DUP; } - if (dev_cnt > 1 && - ((metadata_profile | data_profile) & BTRFS_BLOCK_GROUP_DUP)) { - fprintf(stderr, - "ERROR: DUP is not allowed when FS has multiple devices\n"); - return 1; + if (dev_cnt > 1 && profile & BTRFS_BLOCK_GROUP_DUP) { + warning("DUP is not recommended on filesystem with multiple devices"); } if (metadata_profile & ~allowed) { fprintf(stderr, @@ -2484,6 +1931,12 @@ int test_num_disk_vs_raid(u64 metadata_profile, u64 data_profile, return 1; } + if (dev_cnt == 3 && profile & BTRFS_BLOCK_GROUP_RAID6) { + warning("RAID6 is not recommended on filesystem with 3 devices only"); + } + if (dev_cnt == 2 && profile & BTRFS_BLOCK_GROUP_RAID5) { + warning("RAID5 is not recommended on filesystem with 2 devices only"); + } warning_on(!mixed && (data_profile & BTRFS_BLOCK_GROUP_DUP) && ssd, "DUP may not actually lead to 2 copies on the device, see manual page"); @@ -2508,66 +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(char *file, int force_overwrite) -{ - int ret, fd; - struct stat st; - - ret = is_swap_device(file); - if (ret < 0) { - fprintf(stderr, "ERROR: checking status of %s: %s\n", file, - strerror(-ret)); - return 1; - } - if (ret == 1) { - fprintf(stderr, "ERROR: %s is a swap device\n", file); - return 1; - } - if (!force_overwrite) { - if (check_overwrite(file)) { - fprintf(stderr, "Use the -f option to force overwrite.\n"); - return 1; - } - } - ret = check_mounted(file); - if (ret < 0) { - fprintf(stderr, "ERROR: checking mount status of %s: %s\n", - file, strerror(-ret)); - return 1; - } - if (ret == 1) { - fprintf(stderr, "ERROR: %s is mounted\n", file); - return 1; - } - /* check if the device is busy */ - fd = open(file, O_RDWR|O_EXCL); - if (fd < 0) { - fprintf(stderr, "ERROR: unable to open %s: %s\n", file, - strerror(errno)); - return 1; - } - if (fstat(fd, &st)) { - fprintf(stderr, "ERROR: unable to stat %s: %s\n", file, - strerror(errno)); - close(fd); - return 1; - } - if (!S_ISBLK(st.st_mode)) { - fprintf(stderr, "ERROR: %s is not a block device\n", file); - close(fd); - return 1; - } - close(fd); - return 0; -} - -int btrfs_scan_lblkid(void) +int btrfs_scan_devices(void) { int fd = -1; int ret; @@ -2582,7 +1976,7 @@ int btrfs_scan_lblkid(void) return 0; if (blkid_get_cache(&cache, NULL) < 0) { - printf("ERROR: lblkid cache get failed\n"); + error("blkid cache get failed"); return 1; } blkid_probe_all(cache); @@ -2597,13 +1991,14 @@ int btrfs_scan_lblkid(void) fd = open(path, O_RDONLY); if (fd < 0) { - printf("ERROR: could not open %s\n", path); + error("cannot open %s: %m", path); continue; } ret = btrfs_scan_one_device(fd, path, &tmp_devices, - &num_devices, BTRFS_SUPER_INFO_OFFSET, 0); + &num_devices, BTRFS_SUPER_INFO_OFFSET, + SBREAD_DEFAULT); if (ret) { - printf("ERROR: could not scan %s\n", path); + error("cannot scan %s: %s", path, strerror(-ret)); close (fd); continue; } @@ -2618,41 +2013,12 @@ int btrfs_scan_lblkid(void) return 0; } -int is_vol_small(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 * or y. */ -int ask_user(char *question) +int ask_user(const char *question) { char buf[30] = {0,}; char *saveptr = NULL; @@ -2666,34 +2032,6 @@ int ask_user(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) { - fprintf(stderr, "ERROR: Failed to lookup root id - %s\n", - strerror(errno)); - return ret; - } - - *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 @@ -2748,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: @@ -2823,7 +2140,7 @@ int find_next_key(struct btrfs_path *path, struct btrfs_key *key) return 1; } -char* btrfs_group_type_str(u64 flag) +const char* btrfs_group_type_str(u64 flag) { u64 mask = BTRFS_BLOCK_GROUP_TYPE_MASK | BTRFS_SPACE_INFO_GLOBAL_RSV; @@ -2844,7 +2161,7 @@ char* btrfs_group_type_str(u64 flag) } } -char* btrfs_group_profile_str(u64 flag) +const char* btrfs_group_profile_str(u64 flag) { switch (flag & BTRFS_BLOCK_GROUP_PROFILE_MASK) { case 0: @@ -2866,7 +2183,7 @@ char* btrfs_group_profile_str(u64 flag) } } -u64 disk_size(char *path) +u64 disk_size(const char *path) { struct statfs sfs; @@ -2876,7 +2193,7 @@ u64 disk_size(char *path) return sfs.f_bsize * sfs.f_blocks; } -u64 get_partition_size(char *dev) +u64 get_partition_size(const char *dev) { u64 result; int fd = open(dev, O_RDONLY); @@ -2892,6 +2209,10 @@ u64 get_partition_size(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; @@ -2899,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); @@ -2923,36 +2240,29 @@ 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) { if (nodesize < sectorsize) { - fprintf(stderr, - "ERROR: Illegal nodesize %u (smaller than %u)\n", - nodesize, sectorsize); + error("illegal nodesize %u (smaller than %u)", + nodesize, sectorsize); return -1; } else if (nodesize > BTRFS_MAX_METADATA_BLOCKSIZE) { - fprintf(stderr, - "ERROR: Illegal nodesize %u (larger than %u)\n", + error("illegal nodesize %u (larger than %u)", nodesize, BTRFS_MAX_METADATA_BLOCKSIZE); return -1; } else if (nodesize & (sectorsize - 1)) { - fprintf(stderr, - "ERROR: Illegal nodesize %u (not aligned to %u)\n", + error("illegal nodesize %u (not aligned to %u)", nodesize, sectorsize); return -1; } else if (features & BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS && nodesize != sectorsize) { - fprintf(stderr, - "ERROR: Illegal nodesize %u (not equal to %u for mixed block group)\n", + error("illegal nodesize %u (not equal to %u for mixed block group)", nodesize, sectorsize); return -1; } @@ -2973,7 +2283,7 @@ int arg_copy_path(char *dest, const char *src, int destlen) if (len >= PATH_MAX || len >= destlen) return -ENAMETOOLONG; - __strncpy__null(dest, src, destlen); + __strncpy_null(dest, src, destlen); return 0; } @@ -3083,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') @@ -3094,30 +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) +int prefixcmp(const char *str, const char *prefix) { - 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); - } - } + for (; ; str++, prefix++) + if (!*prefix) + return 0; + else if (*str != *prefix) + return (unsigned char)*prefix - (unsigned char)*str; } /* Subvolume helper functions */ @@ -3133,114 +2454,139 @@ 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; -} - -char *subvol_strip_mountpoint(char *mnt, char *full_path) +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(char *fullpath, struct root_info *get_ri) +/* Set the seed manually */ +void init_rand_seed(u64 seed) { - u64 sv_id; - int ret = 1; - int fd = -1; - int mntfd = -1; - char *mnt = NULL; - char *svpath = NULL; - DIR *dirstream1 = NULL; - DIR *dirstream2 = NULL; + int i; - ret = test_issubvolume(fullpath); - if (ret < 0) - return ret; - if (!ret) { - error("not a subvolume: %s", fullpath); - return 1; + /* only use the last 48 bits */ + for (i = 0; i < 3; i++) { + rand_seed[i] = (unsigned short)(seed ^ (unsigned short)(-1)); + seed >>= 16; } + rand_seed_initlized = 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; +static void __init_seed(void) +{ + struct timeval tv; + int ret; + int fd; + + if(rand_seed_initlized) + return; + /* Use urandom as primary seed source. */ + fd = open("/dev/urandom", O_RDONLY); + if (fd >= 0) { + ret = read(fd, rand_seed, sizeof(rand_seed)); + close(fd); + if (ret < sizeof(rand_seed)) + goto fallback; + } else { +fallback: + /* Use time and pid as fallback seed */ + warning("failed to read /dev/urandom, use time and pid as random seed"); + gettimeofday(&tv, 0); + rand_seed[0] = getpid() ^ (tv.tv_sec & 0xFFFF); + rand_seed[1] = getppid() ^ (tv.tv_usec & 0xFFFF); + rand_seed[2] = (tv.tv_sec ^ tv.tv_usec) >> 16; } - ret = 1; - svpath = subvol_strip_mountpoint(mnt, fullpath); + rand_seed_initlized = 1; +} - fd = btrfs_open_dir(fullpath, &dirstream1, 1); - if (fd < 0) - goto out; +u32 rand_u32(void) +{ + __init_seed(); + /* + * Don't use nrand48, its range is [0,2^31) The highest bit will alwasy + * be 0. Use jrand48 to include the highest bit. + */ + return (u32)jrand48(rand_seed); +} - ret = btrfs_list_get_path_rootid(fd, &sv_id); - if (ret) { - error("can't get rootid for '%s'", fullpath); - goto out; - } +/* Return random number in range [0, upper) */ +unsigned int rand_range(unsigned int upper) +{ + __init_seed(); + /* + * Use the full 48bits to mod, which would be more uniformly + * distributed + */ + return (unsigned int)(jrand48(rand_seed) % upper); +} - mntfd = btrfs_open_dir(mnt, &dirstream2, 1); - if (mntfd < 0) - goto out; +int rand_int(void) +{ + return (int)(rand_u32()); +} - if (sv_id == BTRFS_FS_TREE_OBJECTID) { - ret = 2; - /* - * So that caller may decide if thats an error or just fine. - */ - goto out; - } +u64 rand_u64(void) +{ + u64 ret = 0; - memset(get_ri, 0, sizeof(*get_ri)); - get_ri->root_id = sv_id; + ret += rand_u32(); + ret <<= 32; + ret += rand_u32(); + return ret; +} - ret = btrfs_get_subvol(mntfd, get_ri); - if (ret) - error("can't find '%s': %d", svpath, ret); +u16 rand_u16(void) +{ + return (u16)(rand_u32()); +} -out: - close_file_or_dir(mntfd, dirstream2); - close_file_or_dir(mntfd, dirstream1); - free(mnt); +u8 rand_u8(void) +{ + return (u8)(rand_u32()); +} - return ret; +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"); }