X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=utils.c;h=39b295a4d8b45751c4b40ffffb7ac970aa38e178;hb=00827cad8e36a6f55d344c92a15fb4cd3c596378;hp=f0a3c07093888193d7e04d82561f0d4231f1611c;hpb=80d81228966f4b4ba4886f42f66818e198f454ab;p=platform%2Fupstream%2Fbtrfs-progs.git diff --git a/utils.c b/utils.c index f0a3c07..39b295a 100644 --- a/utils.c +++ b/utils.c @@ -17,10 +17,6 @@ * Boston, MA 021110-1307, USA. */ -#define _XOPEN_SOURCE 700 -#define __USE_XOPEN2K8 -#define __XOPEN2K8 /* due to an error in dirent.h, to get dirfd() */ -#define _GNU_SOURCE /* O_NOATIME */ #include #include #include @@ -38,6 +34,8 @@ #include #include #include +#include + #include "kerncompat.h" #include "radix-tree.h" #include "ctree.h" @@ -52,8 +50,15 @@ #define BLKDISCARD _IO(0x12,119) #endif +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); @@ -64,7 +69,8 @@ void fixup_argv0(char **argv, const char *token) void set_argv0(char **argv) { - sprintf(argv0_buf, "%s", argv[0]); + strncpy(argv0_buf, argv[0], sizeof(argv0_buf)); + argv0_buf[sizeof(argv0_buf) - 1] = 0; } int check_argc_exact(int nargs, int expected) @@ -168,9 +174,10 @@ int test_uuid_unique(char *fs_uuid) return unique; } -int make_btrfs(int fd, const char *device, const char *label, char *fs_uuid, - u64 blocks[7], u64 num_bytes, u32 nodesize, - u32 leafsize, u32 sectorsize, u32 stripesize, u64 features) +/* + * @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 = NULL; @@ -191,57 +198,61 @@ int make_btrfs(int fd, const char *device, const char *label, char *fs_uuid, u64 ref_root; u32 array_size; u32 item_size; - int skinny_metadata = !!(features & + int skinny_metadata = !!(cfg->features & BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA); + u64 num_bytes; - first_free = BTRFS_SUPER_INFO_OFFSET + sectorsize * 2 - 1; - first_free &= ~((u64)sectorsize - 1); + first_free = BTRFS_SUPER_INFO_OFFSET + cfg->sectorsize * 2 - 1; + first_free &= ~((u64)cfg->sectorsize - 1); memset(&super, 0, sizeof(super)); - num_bytes = (num_bytes / sectorsize) * sectorsize; - if (fs_uuid) { - if (uuid_parse(fs_uuid, super.fsid) != 0) { - fprintf(stderr, "could not parse UUID: %s\n", fs_uuid); + 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(fs_uuid)) { - fprintf(stderr, "non-unique UUID: %s\n", fs_uuid); + 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, blocks[0]); + 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, blocks[1]); - btrfs_set_super_chunk_root(&super, blocks[3]); + 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 * leafsize); - btrfs_set_super_sectorsize(&super, sectorsize); - btrfs_set_super_leafsize(&super, leafsize); - btrfs_set_super_nodesize(&super, nodesize); - btrfs_set_super_stripesize(&super, stripesize); + 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, features); - if (label) - strncpy(super.label, label, BTRFS_LABEL_SIZE - 1); + btrfs_set_super_incompat_flags(&super, cfg->features); + if (cfg->label) + strncpy(super.label, cfg->label, BTRFS_LABEL_SIZE - 1); - buf = malloc(sizeof(*buf) + max(sectorsize, leafsize)); + buf = malloc(sizeof(*buf) + max(cfg->sectorsize, cfg->nodesize)); /* create the tree of root objects */ - memset(buf->data, 0, leafsize); - buf->len = leafsize; - btrfs_set_header_bytenr(buf, blocks[1]); + 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); @@ -259,10 +270,10 @@ int make_btrfs(int fd, const char *device, const char *label, char *fs_uuid, 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, leafsize); + 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, leafsize); + btrfs_set_root_used(&root_item, cfg->nodesize); btrfs_set_root_generation(&root_item, 1); memset(&disk_key, 0, sizeof(disk_key)); @@ -270,8 +281,8 @@ int make_btrfs(int fd, const char *device, const char *label, char *fs_uuid, btrfs_set_disk_key_offset(&disk_key, 0); nritems = 0; - itemoff = __BTRFS_LEAF_DATA_SIZE(leafsize) - sizeof(root_item); - btrfs_set_root_bytenr(&root_item, blocks[2]); + 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); @@ -282,7 +293,7 @@ int make_btrfs(int fd, const char *device, const char *label, char *fs_uuid, nritems++; itemoff = itemoff - sizeof(root_item); - btrfs_set_root_bytenr(&root_item, blocks[4]); + 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); @@ -294,7 +305,7 @@ int make_btrfs(int fd, const char *device, const char *label, char *fs_uuid, nritems++; itemoff = itemoff - sizeof(root_item); - btrfs_set_root_bytenr(&root_item, blocks[5]); + 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); @@ -306,7 +317,7 @@ int make_btrfs(int fd, const char *device, const char *label, char *fs_uuid, nritems++; itemoff = itemoff - sizeof(root_item); - btrfs_set_root_bytenr(&root_item, blocks[6]); + 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); @@ -319,28 +330,28 @@ int make_btrfs(int fd, const char *device, const char *label, char *fs_uuid, csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0); - ret = pwrite(fd, buf->data, leafsize, blocks[1]); - if (ret != leafsize) { + 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, - leafsize-sizeof(struct btrfs_header)); + memset(buf->data + sizeof(struct btrfs_header), 0, + cfg->nodesize - sizeof(struct btrfs_header)); nritems = 0; - itemoff = __BTRFS_LEAF_DATA_SIZE(leafsize); + 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(blocks[i] < first_free); - BUG_ON(blocks[i] < blocks[i - 1]); + 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, blocks[i]); + btrfs_set_disk_key_objectid(&disk_key, cfg->blocks[i]); if (skinny_metadata) { btrfs_set_disk_key_type(&disk_key, BTRFS_METADATA_ITEM_KEY); @@ -348,7 +359,7 @@ int make_btrfs(int fd, const char *device, const char *label, char *fs_uuid, } else { btrfs_set_disk_key_type(&disk_key, BTRFS_EXTENT_ITEM_KEY); - btrfs_set_disk_key_offset(&disk_key, leafsize); + 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), @@ -365,7 +376,7 @@ int make_btrfs(int fd, const char *device, const char *label, char *fs_uuid, /* create extent ref */ ref_root = reference_root_table[i]; - btrfs_set_disk_key_objectid(&disk_key, blocks[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); @@ -374,22 +385,22 @@ int make_btrfs(int fd, const char *device, const char *label, char *fs_uuid, btrfs_set_item_size(buf, btrfs_item_nr(nritems), 0); nritems++; } - btrfs_set_header_bytenr(buf, blocks[2]); + 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, leafsize, blocks[2]); - if (ret != leafsize) { + 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, - leafsize-sizeof(struct btrfs_header)); + 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(leafsize) - item_size; + 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); @@ -405,9 +416,9 @@ int make_btrfs(int fd, const char *device, const char *label, char *fs_uuid, 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, sectorsize); - btrfs_set_device_io_width(buf, dev_item, sectorsize); - btrfs_set_device_sector_size(buf, dev_item, sectorsize); + 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, @@ -436,9 +447,9 @@ int make_btrfs(int fd, const char *device, const char *label, char *fs_uuid, 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, sectorsize); - btrfs_set_chunk_io_width(buf, chunk, sectorsize); - btrfs_set_chunk_sector_size(buf, chunk, sectorsize); + 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); @@ -461,21 +472,21 @@ int make_btrfs(int fd, const char *device, const char *label, char *fs_uuid, ptr += item_size; btrfs_set_super_sys_array_size(&super, array_size); - btrfs_set_header_bytenr(buf, blocks[3]); + 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, leafsize, blocks[3]); - if (ret != leafsize) { + 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, - leafsize-sizeof(struct btrfs_header)); + memset(buf->data + sizeof(struct btrfs_header), 0, + cfg->nodesize - sizeof(struct btrfs_header)); nritems = 0; - itemoff = __BTRFS_LEAF_DATA_SIZE(leafsize) - + itemoff = __BTRFS_LEAF_DATA_SIZE(cfg->nodesize) - sizeof(struct btrfs_dev_extent); btrfs_set_disk_key_objectid(&disk_key, 1); @@ -500,49 +511,49 @@ int make_btrfs(int fd, const char *device, const char *label, char *fs_uuid, BTRFS_MKFS_SYSTEM_GROUP_SIZE); nritems++; - btrfs_set_header_bytenr(buf, blocks[4]); + 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, leafsize, blocks[4]); - if (ret != leafsize) { + 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, - leafsize-sizeof(struct btrfs_header)); - btrfs_set_header_bytenr(buf, blocks[5]); + 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, leafsize, blocks[5]); - if (ret != leafsize) { + 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, - leafsize-sizeof(struct btrfs_header)); - btrfs_set_header_bytenr(buf, blocks[6]); + 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, leafsize, blocks[6]); - if (ret != leafsize) { + 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) > sectorsize); - memset(buf->data, 0, sectorsize); + BUG_ON(sizeof(super) > cfg->sectorsize); + memset(buf->data, 0, cfg->sectorsize); memcpy(buf->data, &super, sizeof(super)); - buf->len = sectorsize; + buf->len = cfg->sectorsize; csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0); - ret = pwrite(fd, buf->data, sectorsize, blocks[0]); - if (ret != sectorsize) { + ret = pwrite(fd, buf->data, cfg->sectorsize, cfg->blocks[0]); + if (ret != cfg->sectorsize) { ret = (ret < 0 ? -errno : -EIO); goto out; } @@ -554,6 +565,111 @@ out: 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; @@ -644,6 +760,7 @@ int btrfs_add_to_fsid(struct btrfs_trans_handle *trans, device->bytes_used = 0; device->total_ios = 0; device->dev_root = root->fs_info->dev_root; + device->name = strdup(path); ret = btrfs_add_device(trans, root, device); BUG_ON(ret); @@ -656,9 +773,6 @@ int btrfs_add_to_fsid(struct btrfs_trans_handle *trans, memcpy(disk_super, super, sizeof(*disk_super)); - printf("adding device %s id %llu\n", path, - (unsigned long long)device->devid); - btrfs_set_super_bytenr(disk_super, BTRFS_SUPER_INFO_OFFSET); btrfs_set_stack_device_id(dev_item, device->devid); btrfs_set_stack_device_type(dev_item, device->type); @@ -678,6 +792,42 @@ int btrfs_add_to_fsid(struct btrfs_trans_handle *trans, return 0; } +static void btrfs_wipe_existing_sb(int fd) +{ + const char *off = NULL; + size_t len = 0; + loff_t offset; + char buf[BUFSIZ]; + int rc = 0; + blkid_probe pr = NULL; + + pr = blkid_new_probe(); + if (!pr) + return; + + if (blkid_probe_set_device(pr, fd, 0, 0)) + goto out; + + rc = blkid_probe_lookup_value(pr, "SBMAGIC_OFFSET", &off, NULL); + if (!rc) + rc = blkid_probe_lookup_value(pr, "SBMAGIC", NULL, &len); + + if (rc || len == 0 || off == NULL) + goto out; + + offset = strtoll(off, NULL, 10); + if (len > sizeof(buf)) + len = sizeof(buf); + + memset(buf, 0, len); + rc = pwrite(fd, buf, len, offset); + fsync(fd); + +out: + blkid_free_probe(pr); + return; +} + int btrfs_prepare_device(int fd, char *file, int zero_end, u64 *block_count_ret, u64 max_block_count, int *mixed, int discard) { @@ -709,7 +859,7 @@ int btrfs_prepare_device(int fd, char *file, int zero_end, u64 *block_count_ret, * optimization. */ if (discard_range(fd, 0, 0) == 0) { - fprintf(stderr, "Performing full device TRIM (%s) ...\n", + printf("Performing full device TRIM (%s) ...\n", pretty_size(block_count)); discard_blocks(fd, 0, block_count); } @@ -729,6 +879,8 @@ int btrfs_prepare_device(int fd, char *file, int zero_end, u64 *block_count_ret, return 1; } + btrfs_wipe_existing_sb(fd); + *block_count_ret = block_count; return 0; } @@ -744,7 +896,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->leafsize); + btrfs_set_stack_inode_nbytes(&inode_item, root->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); @@ -811,6 +963,52 @@ int is_mount_point(const char *path) return ret; } +static int is_reg_file(const char *path) +{ + struct stat statbuf; + + if (stat(path, &statbuf) < 0) + return -errno; + return S_ISREG(statbuf.st_mode); +} + +/* + * This function checks if the given input parameter is + * an uuid or a path + * return <0 : some error in the given input + * return BTRFS_ARG_UNKNOWN: unknown input + * return BTRFS_ARG_UUID: given input is uuid + * return BTRFS_ARG_MNTPOINT: given input is path + * return BTRFS_ARG_REG: given input is regular file + */ +int check_arg_type(const char *input) +{ + uuid_t uuid; + char path[PATH_MAX]; + + if (!input) + return -EINVAL; + + if (realpath(input, path)) { + if (is_block_device(path) == 1) + return BTRFS_ARG_BLKDEV; + + if (is_mount_point(path) == 1) + return BTRFS_ARG_MNTPOINT; + + if (is_reg_file(path)) + return BTRFS_ARG_REG; + + return BTRFS_ARG_UNKNOWN; + } + + if (strlen(input) == (BTRFS_UUID_UNPARSED_SIZE - 1) && + !uuid_parse(input, uuid)) + return BTRFS_ARG_UUID; + + return BTRFS_ARG_UNKNOWN; +} + /* * Find the mount point for a mounted device. * On success, returns 0 with mountpoint in *mp. @@ -862,7 +1060,7 @@ out: */ int open_path_or_dev_mnt(const char *path, DIR **dirstream) { - char mp[BTRFS_PATH_NAME_MAX + 1]; + char mp[PATH_MAX]; int fdmnt; if (is_block_device(path)) { @@ -920,7 +1118,8 @@ static int resolve_loop_device(const char* loop_dev, char* loop_file, return 0; } -/* Checks whether a and b are identical or device +/* + * Checks whether a and b are identical or device * files associated with the same block device */ static int is_same_blk_file(const char* a, const char* b) @@ -929,36 +1128,31 @@ static int is_same_blk_file(const char* a, const char* b) char real_a[PATH_MAX]; char real_b[PATH_MAX]; - if(!realpath(a, real_a)) - strcpy(real_a, a); + if (!realpath(a, real_a)) + strncpy_null(real_a, a); if (!realpath(b, real_b)) - strcpy(real_b, b); + strncpy_null(real_b, b); /* Identical path? */ - if(strcmp(real_a, real_b) == 0) + if (strcmp(real_a, real_b) == 0) return 1; - if(stat(a, &st_buf_a) < 0 || - stat(b, &st_buf_b) < 0) - { + if (stat(a, &st_buf_a) < 0 || stat(b, &st_buf_b) < 0) { if (errno == ENOENT) return 0; return -errno; } /* Same blockdevice? */ - if(S_ISBLK(st_buf_a.st_mode) && - S_ISBLK(st_buf_b.st_mode) && - st_buf_a.st_rdev == st_buf_b.st_rdev) - { + if (S_ISBLK(st_buf_a.st_mode) && S_ISBLK(st_buf_b.st_mode) && + st_buf_a.st_rdev == st_buf_b.st_rdev) { return 1; } /* Hardlink? */ if (st_buf_a.st_dev == st_buf_b.st_dev && - st_buf_a.st_ino == st_buf_b.st_ino) - { + st_buf_a.st_ino == st_buf_b.st_ino) { return 1; } @@ -1148,7 +1342,8 @@ int check_mounted_where(int fd, const char *file, char *where, int size, /* scan other devices */ if (is_btrfs && total_devs > 1) { - if ((ret = btrfs_scan_for_fsid(!BTRFS_UPDATE_KERNEL))) + ret = btrfs_scan_lblkid(); + if (ret) return ret; } @@ -1200,150 +1395,58 @@ struct pending_dir { char name[PATH_MAX]; }; -void btrfs_register_one_device(char *fname) +int btrfs_register_one_device(const char *fname) { struct btrfs_ioctl_vol_args args; int fd; int ret; int e; - fd = open("/dev/btrfs-control", O_RDONLY); + fd = open("/dev/btrfs-control", O_RDWR); if (fd < 0) { fprintf(stderr, "failed to open /dev/btrfs-control " "skipping device registration: %s\n", strerror(errno)); - return; + return -errno; } - strncpy(args.name, fname, BTRFS_PATH_NAME_MAX); - args.name[BTRFS_PATH_NAME_MAX-1] = 0; + memset(&args, 0, sizeof(args)); + strncpy_null(args.name, fname); ret = ioctl(fd, BTRFS_IOC_SCAN_DEV, &args); e = errno; - if(ret<0){ + if (ret < 0) { fprintf(stderr, "ERROR: device scan failed '%s' - %s\n", fname, strerror(e)); + ret = -e; } close(fd); + return ret; } -int btrfs_scan_one_dir(char *dirname, int run_ioctl) +/* + * Register all devices in the fs_uuid list created in the user + * space. Ensure btrfs_scan_lblkid() is called before this func. + */ +int btrfs_register_all_devices(void) { - DIR *dirp = NULL; - struct dirent *dirent; - struct pending_dir *pending; - struct stat st; - int ret; - int fd; - int dirname_len; - char *fullpath; - struct list_head pending_list; - struct btrfs_fs_devices *tmp_devices; - u64 num_devices; - - INIT_LIST_HEAD(&pending_list); - - pending = malloc(sizeof(*pending)); - if (!pending) - return -ENOMEM; - strcpy(pending->name, dirname); - -again: - dirname_len = strlen(pending->name); - fullpath = malloc(PATH_MAX); - dirname = pending->name; - - if (!fullpath) { - ret = -ENOMEM; - goto fail; - } - dirp = opendir(dirname); - if (!dirp) { - fprintf(stderr, "Unable to open %s for scanning\n", dirname); - ret = -errno; - goto fail; - } - while(1) { - dirent = readdir(dirp); - if (!dirent) - break; - if (dirent->d_name[0] == '.') - continue; - if (dirname_len + strlen(dirent->d_name) + 2 > PATH_MAX) { - ret = -EFAULT; - goto fail; - } - snprintf(fullpath, PATH_MAX, "%s/%s", dirname, dirent->d_name); - ret = lstat(fullpath, &st); - if (ret < 0) { - fprintf(stderr, "failed to stat %s\n", fullpath); - continue; - } - if (S_ISLNK(st.st_mode)) - continue; - if (S_ISDIR(st.st_mode)) { - struct pending_dir *next = malloc(sizeof(*next)); - if (!next) { - ret = -ENOMEM; - goto fail; + int err; + struct btrfs_fs_devices *fs_devices; + struct btrfs_device *device; + struct list_head *all_uuids; + + all_uuids = btrfs_scanned_uuids(); + + list_for_each_entry(fs_devices, all_uuids, list) { + list_for_each_entry(device, &fs_devices->devices, dev_list) { + if (strlen(device->name) != 0) { + err = btrfs_register_one_device(device->name); + if (err < 0) + return err; + if (err > 0) + return -err; } - strcpy(next->name, fullpath); - list_add_tail(&next->list, &pending_list); - } - if (!S_ISBLK(st.st_mode)) { - continue; - } - fd = open(fullpath, O_RDONLY); - if (fd < 0) { - /* ignore the following errors: - ENXIO (device don't exists) - ENOMEDIUM (No medium found -> - like a cd tray empty) - */ - if(errno != ENXIO && errno != ENOMEDIUM) - fprintf(stderr, "failed to read %s: %s\n", - fullpath, strerror(errno)); - continue; - } - ret = btrfs_scan_one_device(fd, fullpath, &tmp_devices, - &num_devices, - BTRFS_SUPER_INFO_OFFSET, 0); - if (ret == 0 && run_ioctl > 0) { - btrfs_register_one_device(fullpath); } - close(fd); } - if (!list_empty(&pending_list)) { - free(pending); - pending = list_entry(pending_list.next, struct pending_dir, - list); - free(fullpath); - list_del(&pending->list); - closedir(dirp); - dirp = NULL; - goto again; - } - ret = 0; -fail: - free(pending); - free(fullpath); - while (!list_empty(&pending_list)) { - pending = list_entry(pending_list.next, struct pending_dir, - list); - list_del(&pending->list); - free(pending); - } - if (dirp) - closedir(dirp); - return ret; -} - -int btrfs_scan_for_fsid(int run_ioctls) -{ - int ret; - - ret = scan_for_btrfs(BTRFS_SCAN_PROC, run_ioctls); - if (ret) - ret = scan_for_btrfs(BTRFS_SCAN_DEV, run_ioctls); - return ret; + return 0; } int btrfs_device_already_in_root(struct btrfs_root *root, int fd, @@ -1376,35 +1479,94 @@ out: return ret; } -static char *size_strs[] = { "", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"}; -int pretty_size_snprintf(u64 size, char *str, size_t str_bytes) +/* + * Note: this function uses a static per-thread buffer. Do not call this + * function more than 10 times within one argument list! + */ +const char *pretty_size_mode(u64 size, unsigned mode) +{ + static __thread int ps_index = 0; + static __thread char ps_array[10][32]; + char *ret; + + ret = ps_array[ps_index]; + ps_index++; + ps_index %= 10; + (void)pretty_size_snprintf(size, ret, 32, mode); + + return ret; +} + +static const char* unit_suffix_binary[] = + { "B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"}; +static const char* unit_suffix_decimal[] = + { "B", "kB", "MB", "GB", "TB", "PB", "EB"}; + +int pretty_size_snprintf(u64 size, char *str, size_t str_size, unsigned unit_mode) { - int num_divs = 0; + int num_divs; float fraction; + u64 base = 0; + int mult = 0; + const char** suffix = NULL; + u64 last_size; - if (str_bytes == 0) + if (str_size == 0) return 0; - if( size < 1024 ){ - fraction = size; - num_divs = 0; - } else { - u64 last_size = size; - num_divs = 0; - while(size >= 1024){ + if ((unit_mode & ~UNITS_MODE_MASK) == UNITS_RAW) { + snprintf(str, str_size, "%llu", size); + return 0; + } + + if ((unit_mode & ~UNITS_MODE_MASK) == UNITS_BINARY) { + base = 1024; + mult = 1024; + suffix = unit_suffix_binary; + } else if ((unit_mode & ~UNITS_MODE_MASK) == UNITS_DECIMAL) { + base = 1000; + mult = 1000; + suffix = unit_suffix_decimal; + } + + /* Unknown mode */ + if (!base) { + fprintf(stderr, "INTERNAL ERROR: unknown unit base, mode %d\n", + unit_mode); + assert(0); + return -1; + } + + num_divs = 0; + last_size = size; + switch (unit_mode & UNITS_MODE_MASK) { + case UNITS_TBYTES: base *= mult; num_divs++; + case UNITS_GBYTES: base *= mult; num_divs++; + case UNITS_MBYTES: base *= mult; num_divs++; + case UNITS_KBYTES: num_divs++; + break; + case UNITS_BYTES: + base = 1; + num_divs = 0; + break; + default: + while (size >= mult) { last_size = size; - size /= 1024; - num_divs ++; + size /= mult; + num_divs++; } + } - if (num_divs >= ARRAY_SIZE(size_strs)) { - str[0] = '\0'; - return -1; - } - fraction = (float)last_size / 1024; + if (num_divs >= ARRAY_SIZE(unit_suffix_binary)) { + str[0] = '\0'; + printf("INTERNAL ERROR: unsupported unit suffix, index %d\n", + num_divs); + assert(0); + return -1; } - return snprintf(str, str_bytes, "%.2f%s", fraction, - size_strs[num_divs]); + fraction = (float)last_size / base; + + return snprintf(str, str_size, "%.2f%s", fraction, suffix[num_divs]); } /* @@ -1588,87 +1750,23 @@ int set_label(const char *btrfs_dev, const char *label) return ret; } -int btrfs_scan_block_devices(int run_ioctl) +/* + * Unsafe subvolume check. + * + * This only checks ino == BTRFS_FIRST_FREE_OBJECTID, even it is not in a + * btrfs mount point. + * Must use together with other reliable method like btrfs ioctl. + */ +static int __is_subvol(const char *path) { - struct stat st; int ret; - int fd; - struct btrfs_fs_devices *tmp_devices; - u64 num_devices; - FILE *proc_partitions; - int i; - char buf[1024]; - char fullpath[110]; - int scans = 0; - int special; - -scan_again: - proc_partitions = fopen("/proc/partitions","r"); - if (!proc_partitions) { - fprintf(stderr, "Unable to open '/proc/partitions' for scanning\n"); - return -ENOENT; - } - /* skip the header */ - for (i = 0; i < 2; i++) - if (!fgets(buf, 1023, proc_partitions)) { - fprintf(stderr, - "Unable to read '/proc/partitions' for scanning\n"); - fclose(proc_partitions); - return -ENOENT; - } - - strcpy(fullpath,"/dev/"); - while(fgets(buf, 1023, proc_partitions)) { - i = sscanf(buf," %*d %*d %*d %99s", fullpath+5); - - /* - * multipath and MD devices may register as a btrfs filesystem - * both through the original block device and through - * the special (/dev/mapper or /dev/mdX) entry. - * This scans the special entries last - */ - special = strncmp(fullpath, "/dev/dm-", strlen("/dev/dm-")) == 0; - if (!special) - special = strncmp(fullpath, "/dev/md", strlen("/dev/md")) == 0; - - if (scans == 0 && special) - continue; - if (scans > 0 && !special) - continue; - - ret = lstat(fullpath, &st); - if (ret < 0) { - fprintf(stderr, "failed to stat %s\n", fullpath); - continue; - } - if (!S_ISBLK(st.st_mode)) { - continue; - } - fd = open(fullpath, O_RDONLY); - if (fd < 0) { - if (errno != ENOMEDIUM) - fprintf(stderr, "failed to open %s: %s\n", - fullpath, strerror(errno)); - continue; - } - ret = btrfs_scan_one_device(fd, fullpath, &tmp_devices, - &num_devices, - BTRFS_SUPER_INFO_OFFSET, 0); - if (ret == 0 && run_ioctl > 0) { - btrfs_register_one_device(fullpath); - } - close(fd); - } - - fclose(proc_partitions); + ret = lstat(path, &st); + if (ret < 0) + return ret; - if (scans == 0) { - scans++; - goto scan_again; - } - return 0; + return st.st_ino == BTRFS_FIRST_FREE_OBJECTID; } /* @@ -1759,6 +1857,55 @@ u64 parse_size(char *s) return ret; } +u64 parse_qgroupid(const char *p) +{ + char *s = strchr(p, '/'); + const char *ptr_src_end = p + strlen(p); + char *ptr_parse_end = NULL; + u64 level; + u64 id; + int fd; + int ret = 0; + + if (p[0] == '/') + goto path; + + /* Numeric format like '0/257' is the primary case */ + if (!s) { + id = strtoull(p, &ptr_parse_end, 10); + if (ptr_parse_end != ptr_src_end) + goto path; + return id; + } + level = strtoull(p, &ptr_parse_end, 10); + if (ptr_parse_end != s) + goto path; + + id = strtoull(s + 1, &ptr_parse_end, 10); + if (ptr_parse_end != ptr_src_end) + goto path; + + return (level << BTRFS_QGROUP_LEVEL_SHIFT) | id; + +path: + /* Path format like subv at 'my_subvol' is the fallback case */ + ret = __is_subvol(p); + if (ret < 0 || !ret) + goto err; + fd = open(p, O_RDONLY); + if (fd < 0) + goto err; + ret = lookup_ino_rootid(fd, &id); + close(fd); + if (ret < 0) + goto err; + return id; + +err: + fprintf(stderr, "ERROR: invalid qgroupid or subvolume path: %s\n", p); + exit(-1); +} + int open_file_or_dir3(const char *fname, DIR **dirstream, int open_flags) { int ret; @@ -1786,8 +1933,10 @@ int open_file_or_dir3(const char *fname, DIR **dirstream, int open_flags) } if (fd < 0) { fd = -1; - if (*dirstream) + if (*dirstream) { closedir(*dirstream); + *dirstream = NULL; + } } return fd; } @@ -1817,6 +1966,75 @@ int get_device_info(int fd, u64 devid, return ret ? -errno : 0; } +static u64 find_max_device_id(struct btrfs_ioctl_search_args *search_args, + int nr_items) +{ + struct btrfs_dev_item *dev_item; + char *buf = search_args->buf; + + buf += (nr_items - 1) * (sizeof(struct btrfs_ioctl_search_header) + + sizeof(struct btrfs_dev_item)); + buf += sizeof(struct btrfs_ioctl_search_header); + + dev_item = (struct btrfs_dev_item *)buf; + + return btrfs_stack_device_id(dev_item); +} + +static int search_chunk_tree_for_fs_info(int fd, + struct btrfs_ioctl_fs_info_args *fi_args) +{ + int ret; + int max_items; + u64 start_devid = 1; + struct btrfs_ioctl_search_args search_args; + struct btrfs_ioctl_search_key *search_key = &search_args.key; + + fi_args->num_devices = 0; + + max_items = BTRFS_SEARCH_ARGS_BUFSIZE + / (sizeof(struct btrfs_ioctl_search_header) + + sizeof(struct btrfs_dev_item)); + + search_key->tree_id = BTRFS_CHUNK_TREE_OBJECTID; + search_key->min_objectid = BTRFS_DEV_ITEMS_OBJECTID; + search_key->max_objectid = BTRFS_DEV_ITEMS_OBJECTID; + search_key->min_type = BTRFS_DEV_ITEM_KEY; + search_key->max_type = BTRFS_DEV_ITEM_KEY; + search_key->min_transid = 0; + search_key->max_transid = (u64)-1; + search_key->nr_items = max_items; + search_key->max_offset = (u64)-1; + +again: + search_key->min_offset = start_devid; + + ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &search_args); + if (ret < 0) + return -errno; + + fi_args->num_devices += (u64)search_key->nr_items; + + if (search_key->nr_items == max_items) { + start_devid = find_max_device_id(&search_args, + search_key->nr_items) + 1; + goto again; + } + + /* get the lastest max_id to stay consistent with the num_devices */ + if (search_key->nr_items == 0) + /* + * last tree_search returns an empty buf, use the devid of + * the last dev_item of the previous tree_search + */ + fi_args->max_id = start_devid - 1; + else + fi_args->max_id = find_max_device_id(&search_args, + search_key->nr_items); + + return 0; +} + /* * For a given path, fill in the ioctl fs_ and info_ args. * If the path is a btrfs mountpoint, fill info for all devices. @@ -1834,9 +2052,11 @@ int get_fs_info(char *path, struct btrfs_ioctl_fs_info_args *fi_args, int ret = 0; int ndevs = 0; int i = 0; + int replacing = 0; struct btrfs_fs_devices *fs_devices_mnt = NULL; struct btrfs_ioctl_dev_info_args *di_args; - char mp[BTRFS_PATH_NAME_MAX + 1]; + struct btrfs_ioctl_dev_info_args tmp; + char mp[PATH_MAX]; DIR *dirstream = NULL; memset(fi_args, 0, sizeof(*fi_args)); @@ -1896,19 +2116,40 @@ int get_fs_info(char *path, struct btrfs_ioctl_fs_info_args *fi_args, ret = -errno; goto out; } + + /* + * The fs_args->num_devices does not include seed devices + */ + ret = search_chunk_tree_for_fs_info(fd, fi_args); + if (ret) + goto out; + + /* + * search_chunk_tree_for_fs_info() will lacks the devid 0 + * so manual probe for it here. + */ + ret = get_device_info(fd, 0, &tmp); + if (!ret) { + fi_args->num_devices++; + ndevs++; + replacing = 1; + if (i == 0) + i++; + } } if (!fi_args->num_devices) goto out; - di_args = *di_ret = malloc(fi_args->num_devices * sizeof(*di_args)); + di_args = *di_ret = malloc((fi_args->num_devices) * sizeof(*di_args)); if (!di_args) { ret = -errno; goto out; } + if (replacing) + memcpy(di_args, &tmp, sizeof(tmp)); for (; i <= fi_args->max_id; ++i) { - BUG_ON(ndevs >= fi_args->num_devices); ret = get_device_info(fd, i, &di_args[ndevs]); if (ret == -ENODEV) continue; @@ -2086,10 +2327,28 @@ out: return ret; } +static int group_profile_devs_min(u64 flag) +{ + switch (flag & BTRFS_BLOCK_GROUP_PROFILE_MASK) { + case 0: /* single */ + case BTRFS_BLOCK_GROUP_DUP: + return 1; + case BTRFS_BLOCK_GROUP_RAID0: + case BTRFS_BLOCK_GROUP_RAID1: + case BTRFS_BLOCK_GROUP_RAID5: + return 2; + case BTRFS_BLOCK_GROUP_RAID6: + return 3; + case BTRFS_BLOCK_GROUP_RAID10: + return 4; + default: + return -1; + } +} + int test_num_disk_vs_raid(u64 metadata_profile, u64 data_profile, - u64 dev_cnt, int mixed, char *estr) + u64 dev_cnt, int mixed) { - size_t sz = 100; u64 allowed = 0; switch (dev_cnt) { @@ -2106,79 +2365,107 @@ int test_num_disk_vs_raid(u64 metadata_profile, u64 data_profile, 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 (metadata_profile & ~allowed) { - snprintf(estr, sz, "unable to create FS with metadata " - "profile %llu (have %llu devices)\n", - metadata_profile, dev_cnt); + fprintf(stderr, + "ERROR: unable to create FS with metadata profile %s " + "(have %llu devices but %d devices are required)\n", + btrfs_group_profile_str(metadata_profile), dev_cnt, + group_profile_devs_min(metadata_profile)); return 1; } if (data_profile & ~allowed) { - snprintf(estr, sz, "unable to create FS with data " - "profile %llu (have %llu devices)\n", - metadata_profile, dev_cnt); + fprintf(stderr, + "ERROR: unable to create FS with data profile %s " + "(have %llu devices but %d devices are required)\n", + btrfs_group_profile_str(data_profile), dev_cnt, + group_profile_devs_min(data_profile)); return 1; } if (!mixed && (data_profile & BTRFS_BLOCK_GROUP_DUP)) { - snprintf(estr, sz, - "dup for data is allowed only in mixed mode"); + fprintf(stderr, + "ERROR: DUP for data is allowed only in mixed mode"); return 1; } return 0; } -/* Check if disk is suitable for btrfs +int group_profile_max_safe_loss(u64 flags) +{ + switch (flags & BTRFS_BLOCK_GROUP_PROFILE_MASK) { + case 0: /* single */ + case BTRFS_BLOCK_GROUP_DUP: + case BTRFS_BLOCK_GROUP_RAID0: + return 0; + case BTRFS_BLOCK_GROUP_RAID1: + case BTRFS_BLOCK_GROUP_RAID5: + case BTRFS_BLOCK_GROUP_RAID10: + return 1; + case BTRFS_BLOCK_GROUP_RAID6: + return 2; + default: + return -1; + } +} + +/* + * Check if a device is suitable for btrfs * returns: - * 1: something is wrong, estr provides the error + * 1: something is wrong, an error is printed * 0: all is fine */ -int test_dev_for_mkfs(char *file, int force_overwrite, char *estr) +int test_dev_for_mkfs(char *file, int force_overwrite) { int ret, fd; - size_t sz = 100; struct stat st; ret = is_swap_device(file); if (ret < 0) { - snprintf(estr, sz, "error checking %s status: %s\n", file, + fprintf(stderr, "ERROR: checking status of %s: %s\n", file, strerror(-ret)); return 1; } if (ret == 1) { - snprintf(estr, sz, "%s is a swap device\n", file); + fprintf(stderr, "ERROR: %s is a swap device\n", file); return 1; } if (!force_overwrite) { if (check_overwrite(file)) { - snprintf(estr, sz, "Use the -f option to force overwrite.\n"); + fprintf(stderr, "Use the -f option to force overwrite.\n"); return 1; } } ret = check_mounted(file); if (ret < 0) { - snprintf(estr, sz, "error checking %s mount status\n", - file); + fprintf(stderr, "ERROR: checking mount status of %s: %s\n", + file, strerror(-ret)); return 1; } if (ret == 1) { - snprintf(estr, sz, "%s is mounted\n", file); + 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) { - snprintf(estr, sz, "unable to open %s: %s\n", file, + fprintf(stderr, "ERROR: unable to open %s: %s\n", file, strerror(errno)); return 1; } if (fstat(fd, &st)) { - snprintf(estr, sz, "unable to stat %s: %s\n", file, + fprintf(stderr, "ERROR: unable to stat %s: %s\n", file, strerror(errno)); close(fd); return 1; } if (!S_ISBLK(st.st_mode)) { - fprintf(stderr, "'%s' is not a block device\n", file); + fprintf(stderr, "ERROR: %s is not a block device\n", file); close(fd); return 1; } @@ -2186,7 +2473,7 @@ int test_dev_for_mkfs(char *file, int force_overwrite, char *estr) return 0; } -int btrfs_scan_lblkid(int update_kernel) +int btrfs_scan_lblkid() { int fd = -1; int ret; @@ -2197,6 +2484,9 @@ int btrfs_scan_lblkid(int update_kernel) blkid_cache cache = NULL; char path[PATH_MAX]; + if (btrfs_scan_done) + return 0; + if (blkid_get_cache(&cache, 0) < 0) { printf("ERROR: lblkid cache get failed\n"); return 1; @@ -2209,7 +2499,7 @@ int btrfs_scan_lblkid(int update_kernel) if (!dev) continue; /* if we are here its definitely a btrfs disk*/ - strncpy(path, blkid_dev_devname(dev), PATH_MAX); + strncpy_null(path, blkid_dev_devname(dev)); fd = open(path, O_RDONLY); if (fd < 0) { @@ -2225,33 +2515,13 @@ int btrfs_scan_lblkid(int update_kernel) } close(fd); - if (update_kernel) - btrfs_register_one_device(path); } blkid_dev_iterate_end(iter); blkid_put_cache(cache); - return 0; -} -/* - * scans devs for the btrfs -*/ -int scan_for_btrfs(int where, int update_kernel) -{ - int ret = 0; + btrfs_scan_done = 1; - switch (where) { - case BTRFS_SCAN_PROC: - ret = btrfs_scan_block_devices(update_kernel); - break; - case BTRFS_SCAN_DEV: - ret = btrfs_scan_one_dir("/dev", update_kernel); - break; - case BTRFS_SCAN_LBLKID: - ret = btrfs_scan_lblkid(update_kernel); - break; - } - return ret; + return 0; } int is_vol_small(char *file) @@ -2359,8 +2629,8 @@ int find_mount_root(const char *path, char **mount_root) while ((ent = getmntent(mnttab))) { len = strlen(ent->mnt_dir); if (strncmp(ent->mnt_dir, path, len) == 0) { - /* match found */ - if (longest_matchlen < len) { + /* match found and use the latest match */ + if (longest_matchlen <= len) { free(longest_match); longest_matchlen = len; longest_match = strdup(ent->mnt_dir); @@ -2386,7 +2656,7 @@ int find_mount_root(const char *path, char **mount_root) return ret; } -int test_minimum_size(const char *file, u32 leafsize) +int test_minimum_size(const char *file, u32 nodesize) { int fd; struct stat statbuf; @@ -2398,7 +2668,7 @@ int test_minimum_size(const char *file, u32 leafsize) close(fd); return -errno; } - if (btrfs_device_size(fd, &statbuf) < btrfs_min_dev_size(leafsize)) { + if (btrfs_device_size(fd, &statbuf) < btrfs_min_dev_size(nodesize)) { close(fd); return 1; } @@ -2436,3 +2706,187 @@ int test_isdir(const char *path) return S_ISDIR(st.st_mode); } + +void units_set_mode(unsigned *units, unsigned mode) +{ + unsigned base = *units & UNITS_MODE_MASK; + + *units = base | mode; +} + +void units_set_base(unsigned *units, unsigned base) +{ + unsigned mode = *units & ~UNITS_MODE_MASK; + + *units = base | mode; +} + +int find_next_key(struct btrfs_path *path, struct btrfs_key *key) +{ + int level; + + for (level = 0; level < BTRFS_MAX_LEVEL; level++) { + if (!path->nodes[level]) + break; + if (path->slots[level] + 1 >= + btrfs_header_nritems(path->nodes[level])) + continue; + if (level == 0) + btrfs_item_key_to_cpu(path->nodes[level], key, + path->slots[level] + 1); + else + btrfs_node_key_to_cpu(path->nodes[level], key, + path->slots[level] + 1); + return 0; + } + return 1; +} + +char* btrfs_group_type_str(u64 flag) +{ + u64 mask = BTRFS_BLOCK_GROUP_TYPE_MASK | + BTRFS_SPACE_INFO_GLOBAL_RSV; + + switch (flag & mask) { + case BTRFS_BLOCK_GROUP_DATA: + return "Data"; + case BTRFS_BLOCK_GROUP_SYSTEM: + return "System"; + case BTRFS_BLOCK_GROUP_METADATA: + return "Metadata"; + case BTRFS_BLOCK_GROUP_DATA|BTRFS_BLOCK_GROUP_METADATA: + return "Data+Metadata"; + case BTRFS_SPACE_INFO_GLOBAL_RSV: + return "GlobalReserve"; + default: + return "unknown"; + } +} + +char* btrfs_group_profile_str(u64 flag) +{ + switch (flag & BTRFS_BLOCK_GROUP_PROFILE_MASK) { + case 0: + return "single"; + case BTRFS_BLOCK_GROUP_RAID0: + return "RAID0"; + case BTRFS_BLOCK_GROUP_RAID1: + return "RAID1"; + case BTRFS_BLOCK_GROUP_RAID5: + return "RAID5"; + case BTRFS_BLOCK_GROUP_RAID6: + return "RAID6"; + case BTRFS_BLOCK_GROUP_DUP: + return "DUP"; + case BTRFS_BLOCK_GROUP_RAID10: + return "RAID10"; + default: + return "unknown"; + } +} + +u64 disk_size(char *path) +{ + struct statfs sfs; + + if (statfs(path, &sfs) < 0) + return 0; + else + return sfs.f_bsize * sfs.f_blocks; +} + +u64 get_partition_size(char *dev) +{ + u64 result; + int fd = open(dev, O_RDONLY); + + if (fd < 0) + return 0; + if (ioctl(fd, BLKGETSIZE64, &result) < 0) { + close(fd); + return 0; + } + close(fd); + + return result; +} + +int btrfs_tree_search2_ioctl_supported(int fd) +{ + struct btrfs_ioctl_search_args_v2 *args2; + struct btrfs_ioctl_search_key *sk; + 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); + + /* + * Search for the extent tree item in the root tree. + */ + sk->tree_id = BTRFS_ROOT_TREE_OBJECTID; + sk->min_objectid = BTRFS_EXTENT_TREE_OBJECTID; + sk->max_objectid = BTRFS_EXTENT_TREE_OBJECTID; + sk->min_type = BTRFS_ROOT_ITEM_KEY; + sk->max_type = BTRFS_ROOT_ITEM_KEY; + sk->min_offset = 0; + sk->max_offset = (u64)-1; + sk->min_transid = 0; + sk->max_transid = (u64)-1; + sk->nr_items = 1; + 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; + else if (ret == 0) + v2_supported = 1; + else + return ret; + + return v2_supported; +} + +int btrfs_check_nodesize(u32 nodesize, u32 sectorsize) +{ + if (nodesize < sectorsize) { + fprintf(stderr, + "ERROR: Illegal nodesize %u (smaller than %u)\n", + nodesize, sectorsize); + return -1; + } else if (nodesize > BTRFS_MAX_METADATA_BLOCKSIZE) { + fprintf(stderr, + "ERROR: Illegal nodesize %u (larger than %u)\n", + nodesize, BTRFS_MAX_METADATA_BLOCKSIZE); + return -1; + } else if (nodesize & (sectorsize - 1)) { + fprintf(stderr, + "ERROR: Illegal nodesize %u (not aligned to %u)\n", + nodesize, sectorsize); + return -1; + } + return 0; +} + +/* + * Copy a path argument from SRC to DEST and check the SRC length if it's at + * most PATH_MAX and fits into DEST. DESTLEN is supposed to be exact size of + * the buffer. + * The destination buffer is zero terminated. + * Return < 0 for error, 0 otherwise. + */ +int arg_copy_path(char *dest, const char *src, int destlen) +{ + size_t len = strlen(src); + + if (len >= PATH_MAX || len >= destlen) + return -ENAMETOOLONG; + + __strncpy__null(dest, src, destlen); + + return 0; +}