diff options
author | Dimitri John Ledkov <xnox@ubuntu.com> | 2017-07-31 14:54:24 +0100 |
---|---|---|
committer | Dimitri John Ledkov <xnox@ubuntu.com> | 2017-07-31 14:54:24 +0100 |
commit | 6a0440391da7a99ffab94ccc66264af9b5f3ce34 (patch) | |
tree | 1c7cf4f07b08c4965ab19819ebce367be16fb980 /mkfs | |
parent | 5f2e2384443a09e3f1fec71940e9e32b70789102 (diff) |
New upstream release.
Diffstat (limited to 'mkfs')
-rw-r--r-- | mkfs/common.c | 729 | ||||
-rw-r--r-- | mkfs/common.h | 63 | ||||
-rw-r--r-- | mkfs/main.c | 71 |
3 files changed, 826 insertions, 37 deletions
diff --git a/mkfs/common.c b/mkfs/common.c new file mode 100644 index 00000000..e4785c58 --- /dev/null +++ b/mkfs/common.c @@ -0,0 +1,729 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License v2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 021110-1307, USA. + */ + +#include <unistd.h> +#include <uuid/uuid.h> +#include <blkid/blkid.h> +#include <fcntl.h> +#include <limits.h> +#include "ctree.h" +#include "disk-io.h" +#include "volumes.h" +#include "utils.h" +#include "mkfs/common.h" + +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, +}; + +/* + * @fs_uuid - if NULL, generates a UUID, returns back the new filesystem UUID + * + * The superblock signature is not valid, denotes a partially created + * filesystem, needs to be finalized. + */ +int make_btrfs(int fd, struct btrfs_mkfs_config *cfg) +{ + struct btrfs_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) { + if (uuid_parse(cfg->fs_uuid, super.fsid) != 0) { + error("cannot not parse UUID: %s", cfg->fs_uuid); + ret = -EINVAL; + goto out; + } + if (!test_uuid_unique(cfg->fs_uuid)) { + error("non-unique UUID: %s", cfg->fs_uuid); + ret = -EBUSY; + goto out; + } + } else { + uuid_generate(super.fsid); + uuid_unparse(super.fsid, cfg->fs_uuid); + } + uuid_generate(super.dev_item.uuid); + uuid_generate(chunk_tree_uuid); + + cfg->blocks[0] = BTRFS_SUPER_INFO_OFFSET; + for (i = 1; i < 7; i++) { + cfg->blocks[i] = BTRFS_SUPER_INFO_OFFSET + 1024 * 1024 + + cfg->nodesize * i; + } + + btrfs_set_super_bytenr(&super, cfg->blocks[0]); + btrfs_set_super_num_devices(&super, 1); + btrfs_set_super_magic(&super, BTRFS_MAGIC_PARTIAL); + btrfs_set_super_generation(&super, 1); + btrfs_set_super_root(&super, cfg->blocks[1]); + btrfs_set_super_chunk_root(&super, cfg->blocks[3]); + btrfs_set_super_total_bytes(&super, num_bytes); + btrfs_set_super_bytes_used(&super, 6 * cfg->nodesize); + btrfs_set_super_sectorsize(&super, cfg->sectorsize); + super.__unused_leafsize = cpu_to_le32(cfg->nodesize); + btrfs_set_super_nodesize(&super, cfg->nodesize); + btrfs_set_super_stripesize(&super, cfg->stripesize); + btrfs_set_super_csum_type(&super, BTRFS_CSUM_TYPE_CRC32); + btrfs_set_super_chunk_root_generation(&super, 1); + btrfs_set_super_cache_generation(&super, -1); + btrfs_set_super_incompat_flags(&super, cfg->features); + if (cfg->label) + __strncpy_null(super.label, cfg->label, BTRFS_LABEL_SIZE - 1); + + /* create the tree of root objects */ + memset(buf->data, 0, cfg->nodesize); + buf->len = cfg->nodesize; + btrfs_set_header_bytenr(buf, cfg->blocks[1]); + btrfs_set_header_nritems(buf, 4); + btrfs_set_header_generation(buf, 1); + btrfs_set_header_backref_rev(buf, BTRFS_MIXED_BACKREF_REV); + btrfs_set_header_owner(buf, BTRFS_ROOT_TREE_OBJECTID); + write_extent_buffer(buf, super.fsid, btrfs_header_fsid(), + BTRFS_FSID_SIZE); + + write_extent_buffer(buf, chunk_tree_uuid, + btrfs_header_chunk_tree_uuid(buf), + BTRFS_UUID_SIZE); + + /* create the items for the root tree */ + memset(&root_item, 0, sizeof(root_item)); + inode_item = &root_item.inode; + btrfs_set_stack_inode_generation(inode_item, 1); + btrfs_set_stack_inode_size(inode_item, 3); + btrfs_set_stack_inode_nlink(inode_item, 1); + btrfs_set_stack_inode_nbytes(inode_item, cfg->nodesize); + btrfs_set_stack_inode_mode(inode_item, S_IFDIR | 0755); + btrfs_set_root_refs(&root_item, 1); + btrfs_set_root_used(&root_item, cfg->nodesize); + btrfs_set_root_generation(&root_item, 1); + + memset(&disk_key, 0, sizeof(disk_key)); + btrfs_set_disk_key_type(&disk_key, BTRFS_ROOT_ITEM_KEY); + btrfs_set_disk_key_offset(&disk_key, 0); + nritems = 0; + + itemoff = __BTRFS_LEAF_DATA_SIZE(cfg->nodesize) - sizeof(root_item); + btrfs_set_root_bytenr(&root_item, cfg->blocks[2]); + btrfs_set_disk_key_objectid(&disk_key, BTRFS_EXTENT_TREE_OBJECTID); + btrfs_set_item_key(buf, &disk_key, nritems); + btrfs_set_item_offset(buf, btrfs_item_nr(nritems), itemoff); + btrfs_set_item_size(buf, btrfs_item_nr(nritems), + sizeof(root_item)); + write_extent_buffer(buf, &root_item, btrfs_item_ptr_offset(buf, + nritems), sizeof(root_item)); + nritems++; + + itemoff = itemoff - sizeof(root_item); + btrfs_set_root_bytenr(&root_item, cfg->blocks[4]); + btrfs_set_disk_key_objectid(&disk_key, BTRFS_DEV_TREE_OBJECTID); + btrfs_set_item_key(buf, &disk_key, nritems); + btrfs_set_item_offset(buf, btrfs_item_nr(nritems), itemoff); + btrfs_set_item_size(buf, btrfs_item_nr(nritems), + sizeof(root_item)); + write_extent_buffer(buf, &root_item, + btrfs_item_ptr_offset(buf, nritems), + sizeof(root_item)); + nritems++; + + itemoff = itemoff - sizeof(root_item); + btrfs_set_root_bytenr(&root_item, cfg->blocks[5]); + btrfs_set_disk_key_objectid(&disk_key, BTRFS_FS_TREE_OBJECTID); + btrfs_set_item_key(buf, &disk_key, nritems); + btrfs_set_item_offset(buf, btrfs_item_nr(nritems), itemoff); + btrfs_set_item_size(buf, btrfs_item_nr(nritems), + sizeof(root_item)); + write_extent_buffer(buf, &root_item, + btrfs_item_ptr_offset(buf, nritems), + sizeof(root_item)); + nritems++; + + itemoff = itemoff - sizeof(root_item); + btrfs_set_root_bytenr(&root_item, cfg->blocks[6]); + btrfs_set_disk_key_objectid(&disk_key, BTRFS_CSUM_TREE_OBJECTID); + btrfs_set_item_key(buf, &disk_key, nritems); + btrfs_set_item_offset(buf, btrfs_item_nr(nritems), itemoff); + btrfs_set_item_size(buf, btrfs_item_nr(nritems), + sizeof(root_item)); + write_extent_buffer(buf, &root_item, + btrfs_item_ptr_offset(buf, nritems), + sizeof(root_item)); + nritems++; + + + csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0); + ret = pwrite(fd, buf->data, cfg->nodesize, cfg->blocks[1]); + if (ret != cfg->nodesize) { + ret = (ret < 0 ? -errno : -EIO); + goto out; + } + + /* create the items for the extent tree */ + memset(buf->data + sizeof(struct btrfs_header), 0, + cfg->nodesize - sizeof(struct btrfs_header)); + nritems = 0; + itemoff = __BTRFS_LEAF_DATA_SIZE(cfg->nodesize); + for (i = 1; i < 7; i++) { + item_size = sizeof(struct btrfs_extent_item); + if (!skinny_metadata) + item_size += sizeof(struct btrfs_tree_block_info); + + if (cfg->blocks[i] < first_free) { + error("block[%d] below first free: %llu < %llu", + i, (unsigned long long)cfg->blocks[i], + (unsigned long long)first_free); + ret = -EINVAL; + goto out; + } + if (cfg->blocks[i] < cfg->blocks[i - 1]) { + error("blocks %d and %d in reverse order: %llu < %llu", + i, i - 1, + (unsigned long long)cfg->blocks[i], + (unsigned long long)cfg->blocks[i - 1]); + ret = -EINVAL; + goto out; + } + + /* create extent item */ + itemoff -= item_size; + btrfs_set_disk_key_objectid(&disk_key, cfg->blocks[i]); + if (skinny_metadata) { + btrfs_set_disk_key_type(&disk_key, + BTRFS_METADATA_ITEM_KEY); + btrfs_set_disk_key_offset(&disk_key, 0); + } else { + btrfs_set_disk_key_type(&disk_key, + BTRFS_EXTENT_ITEM_KEY); + btrfs_set_disk_key_offset(&disk_key, cfg->nodesize); + } + btrfs_set_item_key(buf, &disk_key, nritems); + btrfs_set_item_offset(buf, btrfs_item_nr(nritems), + itemoff); + btrfs_set_item_size(buf, btrfs_item_nr(nritems), + item_size); + extent_item = btrfs_item_ptr(buf, nritems, + struct btrfs_extent_item); + btrfs_set_extent_refs(buf, extent_item, 1); + btrfs_set_extent_generation(buf, extent_item, 1); + btrfs_set_extent_flags(buf, extent_item, + BTRFS_EXTENT_FLAG_TREE_BLOCK); + nritems++; + + /* create extent ref */ + ref_root = reference_root_table[i]; + btrfs_set_disk_key_objectid(&disk_key, cfg->blocks[i]); + btrfs_set_disk_key_offset(&disk_key, ref_root); + btrfs_set_disk_key_type(&disk_key, BTRFS_TREE_BLOCK_REF_KEY); + btrfs_set_item_key(buf, &disk_key, nritems); + btrfs_set_item_offset(buf, btrfs_item_nr(nritems), + itemoff); + btrfs_set_item_size(buf, btrfs_item_nr(nritems), 0); + nritems++; + } + btrfs_set_header_bytenr(buf, cfg->blocks[2]); + btrfs_set_header_owner(buf, BTRFS_EXTENT_TREE_OBJECTID); + btrfs_set_header_nritems(buf, nritems); + csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0); + ret = pwrite(fd, buf->data, cfg->nodesize, cfg->blocks[2]); + if (ret != cfg->nodesize) { + ret = (ret < 0 ? -errno : -EIO); + goto out; + } + + /* create the chunk tree */ + memset(buf->data + sizeof(struct btrfs_header), 0, + cfg->nodesize - sizeof(struct btrfs_header)); + nritems = 0; + item_size = sizeof(*dev_item); + itemoff = __BTRFS_LEAF_DATA_SIZE(cfg->nodesize) - item_size; + + /* first device 1 (there is no device 0) */ + btrfs_set_disk_key_objectid(&disk_key, BTRFS_DEV_ITEMS_OBJECTID); + btrfs_set_disk_key_offset(&disk_key, 1); + btrfs_set_disk_key_type(&disk_key, BTRFS_DEV_ITEM_KEY); + btrfs_set_item_key(buf, &disk_key, nritems); + btrfs_set_item_offset(buf, btrfs_item_nr(nritems), itemoff); + btrfs_set_item_size(buf, btrfs_item_nr(nritems), item_size); + + dev_item = btrfs_item_ptr(buf, nritems, struct btrfs_dev_item); + btrfs_set_device_id(buf, dev_item, 1); + btrfs_set_device_generation(buf, dev_item, 0); + btrfs_set_device_total_bytes(buf, dev_item, num_bytes); + btrfs_set_device_bytes_used(buf, dev_item, + BTRFS_MKFS_SYSTEM_GROUP_SIZE); + btrfs_set_device_io_align(buf, dev_item, cfg->sectorsize); + btrfs_set_device_io_width(buf, dev_item, cfg->sectorsize); + btrfs_set_device_sector_size(buf, dev_item, cfg->sectorsize); + btrfs_set_device_type(buf, dev_item, 0); + + write_extent_buffer(buf, super.dev_item.uuid, + (unsigned long)btrfs_device_uuid(dev_item), + BTRFS_UUID_SIZE); + write_extent_buffer(buf, super.fsid, + (unsigned long)btrfs_device_fsid(dev_item), + BTRFS_UUID_SIZE); + read_extent_buffer(buf, &super.dev_item, (unsigned long)dev_item, + sizeof(*dev_item)); + + nritems++; + item_size = btrfs_chunk_item_size(1); + itemoff = itemoff - item_size; + + /* then we have chunk 0 */ + btrfs_set_disk_key_objectid(&disk_key, BTRFS_FIRST_CHUNK_TREE_OBJECTID); + btrfs_set_disk_key_offset(&disk_key, 0); + btrfs_set_disk_key_type(&disk_key, BTRFS_CHUNK_ITEM_KEY); + btrfs_set_item_key(buf, &disk_key, nritems); + btrfs_set_item_offset(buf, btrfs_item_nr(nritems), itemoff); + btrfs_set_item_size(buf, btrfs_item_nr(nritems), item_size); + + chunk = btrfs_item_ptr(buf, nritems, struct btrfs_chunk); + btrfs_set_chunk_length(buf, chunk, BTRFS_MKFS_SYSTEM_GROUP_SIZE); + btrfs_set_chunk_owner(buf, chunk, BTRFS_EXTENT_TREE_OBJECTID); + btrfs_set_chunk_stripe_len(buf, chunk, BTRFS_STRIPE_LEN); + btrfs_set_chunk_type(buf, chunk, BTRFS_BLOCK_GROUP_SYSTEM); + btrfs_set_chunk_io_align(buf, chunk, cfg->sectorsize); + btrfs_set_chunk_io_width(buf, chunk, cfg->sectorsize); + btrfs_set_chunk_sector_size(buf, chunk, cfg->sectorsize); + btrfs_set_chunk_num_stripes(buf, chunk, 1); + btrfs_set_stripe_devid_nr(buf, chunk, 0, 1); + btrfs_set_stripe_offset_nr(buf, chunk, 0, 0); + nritems++; + + write_extent_buffer(buf, super.dev_item.uuid, + (unsigned long)btrfs_stripe_dev_uuid(&chunk->stripe), + BTRFS_UUID_SIZE); + + /* copy the key for the chunk to the system array */ + ptr = super.sys_chunk_array; + array_size = sizeof(disk_key); + + memcpy(ptr, &disk_key, sizeof(disk_key)); + ptr += sizeof(disk_key); + + /* copy the chunk to the system array */ + read_extent_buffer(buf, ptr, (unsigned long)chunk, item_size); + array_size += item_size; + ptr += item_size; + btrfs_set_super_sys_array_size(&super, array_size); + + btrfs_set_header_bytenr(buf, cfg->blocks[3]); + btrfs_set_header_owner(buf, BTRFS_CHUNK_TREE_OBJECTID); + btrfs_set_header_nritems(buf, nritems); + csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0); + ret = pwrite(fd, buf->data, cfg->nodesize, cfg->blocks[3]); + if (ret != cfg->nodesize) { + ret = (ret < 0 ? -errno : -EIO); + goto out; + } + + /* create the device tree */ + memset(buf->data + sizeof(struct btrfs_header), 0, + cfg->nodesize - sizeof(struct btrfs_header)); + nritems = 0; + itemoff = __BTRFS_LEAF_DATA_SIZE(cfg->nodesize) - + sizeof(struct btrfs_dev_extent); + + btrfs_set_disk_key_objectid(&disk_key, 1); + btrfs_set_disk_key_offset(&disk_key, 0); + btrfs_set_disk_key_type(&disk_key, BTRFS_DEV_EXTENT_KEY); + btrfs_set_item_key(buf, &disk_key, nritems); + btrfs_set_item_offset(buf, btrfs_item_nr(nritems), itemoff); + btrfs_set_item_size(buf, btrfs_item_nr(nritems), + sizeof(struct btrfs_dev_extent)); + dev_extent = btrfs_item_ptr(buf, nritems, struct btrfs_dev_extent); + btrfs_set_dev_extent_chunk_tree(buf, dev_extent, + BTRFS_CHUNK_TREE_OBJECTID); + btrfs_set_dev_extent_chunk_objectid(buf, dev_extent, + BTRFS_FIRST_CHUNK_TREE_OBJECTID); + btrfs_set_dev_extent_chunk_offset(buf, dev_extent, 0); + + write_extent_buffer(buf, chunk_tree_uuid, + (unsigned long)btrfs_dev_extent_chunk_tree_uuid(dev_extent), + BTRFS_UUID_SIZE); + + btrfs_set_dev_extent_length(buf, dev_extent, + BTRFS_MKFS_SYSTEM_GROUP_SIZE); + nritems++; + + btrfs_set_header_bytenr(buf, cfg->blocks[4]); + btrfs_set_header_owner(buf, BTRFS_DEV_TREE_OBJECTID); + btrfs_set_header_nritems(buf, nritems); + csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0); + ret = pwrite(fd, buf->data, cfg->nodesize, cfg->blocks[4]); + if (ret != cfg->nodesize) { + ret = (ret < 0 ? -errno : -EIO); + goto out; + } + + /* create the FS root */ + memset(buf->data + sizeof(struct btrfs_header), 0, + cfg->nodesize - sizeof(struct btrfs_header)); + btrfs_set_header_bytenr(buf, cfg->blocks[5]); + btrfs_set_header_owner(buf, BTRFS_FS_TREE_OBJECTID); + btrfs_set_header_nritems(buf, 0); + csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0); + ret = pwrite(fd, buf->data, cfg->nodesize, cfg->blocks[5]); + if (ret != cfg->nodesize) { + ret = (ret < 0 ? -errno : -EIO); + goto out; + } + /* finally create the csum root */ + memset(buf->data + sizeof(struct btrfs_header), 0, + cfg->nodesize - sizeof(struct btrfs_header)); + btrfs_set_header_bytenr(buf, cfg->blocks[6]); + btrfs_set_header_owner(buf, BTRFS_CSUM_TREE_OBJECTID); + btrfs_set_header_nritems(buf, 0); + csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0); + ret = pwrite(fd, buf->data, cfg->nodesize, cfg->blocks[6]); + if (ret != cfg->nodesize) { + ret = (ret < 0 ? -errno : -EIO); + goto out; + } + + /* and write out the super block */ + memset(buf->data, 0, BTRFS_SUPER_INFO_SIZE); + memcpy(buf->data, &super, sizeof(super)); + buf->len = BTRFS_SUPER_INFO_SIZE; + csum_tree_block_size(buf, BTRFS_CRC32_SIZE, 0); + ret = pwrite(fd, buf->data, BTRFS_SUPER_INFO_SIZE, cfg->blocks[0]); + if (ret != BTRFS_SUPER_INFO_SIZE) { + ret = (ret < 0 ? -errno : -EIO); + goto out; + } + + ret = 0; + +out: + free(buf); + return ret; +} + +u64 btrfs_min_dev_size(u32 nodesize) +{ + return 2 * (BTRFS_MKFS_SYSTEM_GROUP_SIZE + + btrfs_min_global_blk_rsv_size(nodesize)); +} + +/* + * Btrfs minimum size calculation is complicated, it should include at least: + * 1. system group size + * 2. minimum global block reserve + * 3. metadata used at mkfs + * 4. space reservation to create uuid for first mount. + * Also, raid factor should also be taken into consideration. + * To avoid the overkill calculation, (system group + global block rsv) * 2 + * for *EACH* device should be good enough. + */ +u64 btrfs_min_global_blk_rsv_size(u32 nodesize) +{ + return (u64)nodesize << 10; +} + +#define isoctal(c) (((c) & ~7) == '0') + +static inline void translate(char *f, char *t) +{ + while (*f != '\0') { + if (*f == '\\' && + isoctal(f[1]) && isoctal(f[2]) && isoctal(f[3])) { + *t++ = 64*(f[1] & 7) + 8*(f[2] & 7) + (f[3] & 7); + f += 4; + } else + *t++ = *f++; + } + *t = '\0'; + return; +} + +/* + * Checks if the swap device. + * Returns 1 if swap device, < 0 on error or 0 if not swap device. + */ +static int is_swap_device(const char *file) +{ + 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; + + /* skip the first line */ + if (fgets(tmp, sizeof(tmp), f) == NULL) + 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; + } + } + } + +out: + fclose(f); + + return ret; +} + +/* + * Check for existing filesystem or partition table on device. + * Returns: + * 1 for existing fs or partition + * 0 for nothing found + * -1 for internal error + */ +static int check_overwrite(const char *device) +{ + const char *type; + blkid_probe pr = NULL; + int ret; + blkid_loff_t size; + + if (!device || !*device) + return 0; + + ret = -1; /* will reset on success of all setup calls */ + + pr = blkid_new_probe_from_filename(device); + if (!pr) + goto out; + + size = blkid_probe_get_size(pr); + if (size < 0) + goto out; + + /* nothing to overwrite on a 0-length device */ + if (size == 0) { + ret = 0; + goto out; + } + + ret = blkid_probe_enable_partitions(pr, 1); + if (ret < 0) + goto out; + + ret = blkid_do_fullprobe(pr); + if (ret < 0) + goto out; + + /* + * 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; + } + + 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; + +out: + if (pr) + blkid_free_probe(pr); + if (ret == -1) + fprintf(stderr, + "probe of %s failed, cannot detect " + "existing filesystem.\n", device); + return ret; +} + +/* + * Check if a device is suitable for btrfs + * returns: + * 1: something is wrong, an error is printed + * 0: all is fine + */ +int test_dev_for_mkfs(const char *file, int force_overwrite) +{ + int ret, fd; + struct stat st; + + ret = is_swap_device(file); + if (ret < 0) { + error("checking status of %s: %s", file, strerror(-ret)); + return 1; + } + if (ret == 1) { + error("%s is a swap device", file); + return 1; + } + if (!force_overwrite) { + if (check_overwrite(file)) { + error("use the -f option to force overwrite of %s", + file); + return 1; + } + } + ret = check_mounted(file); + if (ret < 0) { + error("cannot check mount status of %s: %s", file, + strerror(-ret)); + return 1; + } + if (ret == 1) { + error("%s is mounted", file); + return 1; + } + /* check if the device is busy */ + fd = open(file, O_RDWR|O_EXCL); + if (fd < 0) { + error("unable to open %s: %s", file, strerror(errno)); + return 1; + } + if (fstat(fd, &st)) { + error("unable to stat %s: %s", file, strerror(errno)); + close(fd); + return 1; + } + if (!S_ISBLK(st.st_mode)) { + error("%s is not a block device", file); + close(fd); + return 1; + } + close(fd); + return 0; +} + +int is_vol_small(const char *file) +{ + int fd = -1; + int e; + struct stat st; + u64 size; + + fd = open(file, O_RDONLY); + if (fd < 0) + return -errno; + if (fstat(fd, &st) < 0) { + e = -errno; + close(fd); + return e; + } + size = btrfs_device_size(fd, &st); + if (size == 0) { + close(fd); + return -1; + } + if (size < BTRFS_MKFS_SMALL_VOLUME_SIZE) { + close(fd); + return 1; + } else { + close(fd); + return 0; + } +} + +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; +} + + diff --git a/mkfs/common.h b/mkfs/common.h new file mode 100644 index 00000000..666a75b3 --- /dev/null +++ b/mkfs/common.h @@ -0,0 +1,63 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License v2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 021110-1307, USA. + */ + +/* + * Defines and function declarations for users of the mkfs API, no internal + * defintions. + */ + +#ifndef __BTRFS_MKFS_COMMON_H__ +#define __BTRFS_MKFS_COMMON_H__ + +#include "kerncompat.h" +#include "common-defs.h" + +#define BTRFS_MKFS_SYSTEM_GROUP_SIZE SZ_4M +#define BTRFS_MKFS_SMALL_VOLUME_SIZE SZ_1G + +struct btrfs_mkfs_config { + /* Label of the new filesystem */ + const char *label; + /* Blck sizes */ + u32 nodesize; + u32 sectorsize; + u32 stripesize; + /* Bitfield of incompat features, BTRFS_FEATURE_INCOMPAT_* */ + u64 features; + /* Size of the filesystem in bytes */ + u64 num_bytes; + + /* Output fields, set during creation */ + + /* Logical addresses of superblock [0] and other tree roots */ + u64 blocks[8]; + char fs_uuid[BTRFS_UUID_UNPARSED_SIZE]; + char chunk_uuid[BTRFS_UUID_UNPARSED_SIZE]; + + /* Superblock offset after make_btrfs */ + u64 super_bytenr; +}; + +int make_btrfs(int fd, struct btrfs_mkfs_config *cfg); +u64 btrfs_min_dev_size(u32 nodesize); +u64 btrfs_min_global_blk_rsv_size(u32 nodesize); +int test_minimum_size(const char *file, u32 nodesize); +int is_vol_small(const char *file); +int test_num_disk_vs_raid(u64 metadata_profile, u64 data_profile, + u64 dev_cnt, int mixed, int ssd); +int test_dev_for_mkfs(const char *file, int force_overwrite); + +#endif diff --git a/mkfs/main.c b/mkfs/main.c index 72834c9c..2b109a53 100644 --- a/mkfs/main.c +++ b/mkfs/main.c @@ -43,6 +43,9 @@ #include "transaction.h" #include "utils.h" #include "list_sort.h" +#include "help.h" +#include "mkfs/common.h" +#include "fsfeatures.h" static u64 index_cnt = 2; static int verbose = 1; @@ -64,6 +67,7 @@ struct mkfs_allocation { static int create_metadata_block_groups(struct btrfs_root *root, int mixed, struct mkfs_allocation *allocation) { + struct btrfs_fs_info *fs_info = root->fs_info; struct btrfs_trans_handle *trans; u64 bytes_used; u64 chunk_start = 0; @@ -71,10 +75,10 @@ static int create_metadata_block_groups(struct btrfs_root *root, int mixed, int ret; trans = btrfs_start_transaction(root, 1); - bytes_used = btrfs_super_bytes_used(root->fs_info->super_copy); + bytes_used = btrfs_super_bytes_used(fs_info->super_copy); root->fs_info->system_allocs = 1; - ret = btrfs_make_block_group(trans, root, bytes_used, + ret = btrfs_make_block_group(trans, fs_info, bytes_used, BTRFS_BLOCK_GROUP_SYSTEM, BTRFS_FIRST_CHUNK_TREE_OBJECTID, 0, BTRFS_MKFS_SYSTEM_GROUP_SIZE); @@ -83,7 +87,7 @@ static int create_metadata_block_groups(struct btrfs_root *root, int mixed, return ret; if (mixed) { - ret = btrfs_alloc_chunk(trans, root->fs_info->extent_root, + ret = btrfs_alloc_chunk(trans, fs_info, &chunk_start, &chunk_size, BTRFS_BLOCK_GROUP_METADATA | BTRFS_BLOCK_GROUP_DATA); @@ -93,7 +97,7 @@ static int create_metadata_block_groups(struct btrfs_root *root, int mixed, } if (ret) return ret; - ret = btrfs_make_block_group(trans, root, 0, + ret = btrfs_make_block_group(trans, fs_info, 0, BTRFS_BLOCK_GROUP_METADATA | BTRFS_BLOCK_GROUP_DATA, BTRFS_FIRST_CHUNK_TREE_OBJECTID, @@ -102,7 +106,7 @@ static int create_metadata_block_groups(struct btrfs_root *root, int mixed, return ret; allocation->mixed += chunk_size; } else { - ret = btrfs_alloc_chunk(trans, root->fs_info->extent_root, + ret = btrfs_alloc_chunk(trans, fs_info, &chunk_start, &chunk_size, BTRFS_BLOCK_GROUP_METADATA); if (ret == -ENOSPC) { @@ -111,7 +115,7 @@ static int create_metadata_block_groups(struct btrfs_root *root, int mixed, } if (ret) return ret; - ret = btrfs_make_block_group(trans, root, 0, + ret = btrfs_make_block_group(trans, fs_info, 0, BTRFS_BLOCK_GROUP_METADATA, BTRFS_FIRST_CHUNK_TREE_OBJECTID, chunk_start, chunk_size); @@ -131,12 +135,13 @@ static int create_data_block_groups(struct btrfs_trans_handle *trans, struct btrfs_root *root, int mixed, struct mkfs_allocation *allocation) { + struct btrfs_fs_info *fs_info = root->fs_info; u64 chunk_start = 0; u64 chunk_size = 0; int ret = 0; if (!mixed) { - ret = btrfs_alloc_chunk(trans, root->fs_info->extent_root, + ret = btrfs_alloc_chunk(trans, fs_info, &chunk_start, &chunk_size, BTRFS_BLOCK_GROUP_DATA); if (ret == -ENOSPC) { @@ -145,7 +150,7 @@ static int create_data_block_groups(struct btrfs_trans_handle *trans, } if (ret) return ret; - ret = btrfs_make_block_group(trans, root, 0, + ret = btrfs_make_block_group(trans, fs_info, 0, BTRFS_BLOCK_GROUP_DATA, BTRFS_FIRST_CHUNK_TREE_OBJECTID, chunk_start, chunk_size); @@ -158,8 +163,8 @@ err: return ret; } -static int make_root_dir(struct btrfs_trans_handle *trans, struct btrfs_root *root, - struct mkfs_allocation *allocation) +static int make_root_dir(struct btrfs_trans_handle *trans, + struct btrfs_root *root) { struct btrfs_key location; int ret; @@ -241,11 +246,12 @@ static int create_one_raid_group(struct btrfs_trans_handle *trans, struct mkfs_allocation *allocation) { + struct btrfs_fs_info *fs_info = root->fs_info; u64 chunk_start; u64 chunk_size; int ret; - ret = btrfs_alloc_chunk(trans, root->fs_info->extent_root, + ret = btrfs_alloc_chunk(trans, fs_info, &chunk_start, &chunk_size, type); if (ret == -ENOSPC) { error("not enough free space to allocate chunk"); @@ -254,7 +260,7 @@ static int create_one_raid_group(struct btrfs_trans_handle *trans, if (ret) return ret; - ret = btrfs_make_block_group(trans, root->fs_info->extent_root, 0, + ret = btrfs_make_block_group(trans, fs_info, 0, type, BTRFS_FIRST_CHUNK_TREE_OBJECTID, chunk_start, chunk_size); @@ -448,7 +454,7 @@ static int fill_inode_item(struct btrfs_trans_handle *trans, struct btrfs_inode_item *dst, struct stat *src) { u64 blocks = 0; - u64 sectorsize = root->sectorsize; + u64 sectorsize = root->fs_info->sectorsize; /* * btrfs_inode_item has some reserved fields @@ -541,8 +547,8 @@ static u64 calculate_dir_inode_size(const char *dirname) static int add_inode_items(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct stat *st, const char *name, - u64 self_objectid, ino_t parent_inum, - int dir_index_cnt, struct btrfs_inode_item *inode_ret) + u64 self_objectid, + struct btrfs_inode_item *inode_ret) { int ret; struct btrfs_inode_item btrfs_inode; @@ -642,15 +648,14 @@ fail: static int add_file_items(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct btrfs_inode_item *btrfs_inode, u64 objectid, - ino_t parent_inum, struct stat *st, - const char *path_name, int out_fd) + struct stat *st, const char *path_name) { int ret = -1; ssize_t ret_read; u64 bytes_read = 0; struct btrfs_key key; int blocks; - u32 sectorsize = root->sectorsize; + u32 sectorsize = root->fs_info->sectorsize; u64 first_block = 0; u64 file_pos = 0; u64 cur_bytes; @@ -751,7 +756,7 @@ again: if (ret) goto end; - ret = write_and_map_eb(trans, root, eb); + ret = write_and_map_eb(root->fs_info, eb); if (ret) { error("failed to write %s", path_name); goto end; @@ -901,7 +906,6 @@ static int traverse_directory(struct btrfs_trans_handle *trans, ret = add_inode_items(trans, root, &st, cur_file->d_name, cur_inum, - parent_inum, dir_index_cnt, &cur_inode); if (ret == -EEXIST) { if (st.st_nlink <= 1) { @@ -941,8 +945,8 @@ static int traverse_directory(struct btrfs_trans_handle *trans, list_add_tail(&dir_entry->list, &dir_head->list); } else if (S_ISREG(st.st_mode)) { ret = add_file_items(trans, root, &cur_inode, - cur_inum, parent_inum, &st, - cur_file->d_name, out_fd); + cur_inum, &st, + cur_file->d_name); if (ret) { error("unable to add file items for %s: %d", cur_file->d_name, ret); @@ -983,6 +987,7 @@ static int create_chunks(struct btrfs_trans_handle *trans, u64 size_of_data, struct mkfs_allocation *allocation) { + struct btrfs_fs_info *fs_info = root->fs_info; u64 chunk_start; u64 chunk_size; u64 meta_type = BTRFS_BLOCK_GROUP_METADATA; @@ -992,35 +997,35 @@ static int create_chunks(struct btrfs_trans_handle *trans, int ret; for (i = 0; i < num_of_meta_chunks; i++) { - ret = btrfs_alloc_chunk(trans, root->fs_info->extent_root, + ret = btrfs_alloc_chunk(trans, fs_info, &chunk_start, &chunk_size, meta_type); if (ret) return ret; - ret = btrfs_make_block_group(trans, root->fs_info->extent_root, 0, + ret = btrfs_make_block_group(trans, fs_info, 0, meta_type, BTRFS_FIRST_CHUNK_TREE_OBJECTID, chunk_start, chunk_size); allocation->metadata += chunk_size; if (ret) return ret; set_extent_dirty(&root->fs_info->free_space_cache, - chunk_start, chunk_start + chunk_size - 1, 0); + chunk_start, chunk_start + chunk_size - 1); } if (size_of_data < minimum_data_chunk_size) size_of_data = minimum_data_chunk_size; - ret = btrfs_alloc_data_chunk(trans, root->fs_info->extent_root, + ret = btrfs_alloc_data_chunk(trans, fs_info, &chunk_start, size_of_data, data_type, 0); if (ret) return ret; - ret = btrfs_make_block_group(trans, root->fs_info->extent_root, 0, + ret = btrfs_make_block_group(trans, fs_info, 0, data_type, BTRFS_FIRST_CHUNK_TREE_OBJECTID, chunk_start, size_of_data); allocation->data += size_of_data; if (ret) return ret; set_extent_dirty(&root->fs_info->free_space_cache, - chunk_start, chunk_start + size_of_data - 1, 0); + chunk_start, chunk_start + size_of_data - 1); return ret; } @@ -1397,7 +1402,6 @@ int main(int argc, char **argv) char *label = NULL; u64 block_count = 0; u64 dev_block_count = 0; - u64 blocks[7]; u64 alloc_start = 0; u64 metadata_profile = 0; u64 data_profile = 0; @@ -1720,12 +1724,6 @@ int main(int argc, char **argv) exit(1); } - blocks[0] = BTRFS_SUPER_INFO_OFFSET; - for (i = 1; i < 7; i++) { - blocks[i] = BTRFS_SUPER_INFO_OFFSET + 1024 * 1024 + - nodesize * i; - } - if (group_profile_max_safe_loss(metadata_profile) < group_profile_max_safe_loss(data_profile)){ warning("metadata has lower redundancy than data!\n"); @@ -1733,7 +1731,6 @@ int main(int argc, char **argv) mkfs_cfg.label = label; memcpy(mkfs_cfg.fs_uuid, fs_uuid, sizeof(mkfs_cfg.fs_uuid)); - memcpy(mkfs_cfg.blocks, blocks, sizeof(blocks)); mkfs_cfg.num_bytes = dev_block_count; mkfs_cfg.nodesize = nodesize; mkfs_cfg.sectorsize = sectorsize; @@ -1774,7 +1771,7 @@ int main(int argc, char **argv) exit(1); } - ret = make_root_dir(trans, root, &allocation); + ret = make_root_dir(trans, root); if (ret) { error("failed to setup the root directory: %d", ret); exit(1); |