#include "transaction.h"
#include "utils.h"
#include "list_sort.h"
+#include "help.h"
+#include "mkfs/common.h"
+#include "fsfeatures.h"
+
+int path_cat_out(char *out, const char *p1, const char *p2);
static u64 index_cnt = 2;
static int verbose = 1;
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;
int ret;
trans = btrfs_start_transaction(root, 1);
- bytes_used = btrfs_super_bytes_used(root->fs_info->super_copy);
+ BUG_ON(IS_ERR(trans));
+ 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);
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);
}
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,
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) {
}
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);
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) {
}
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);
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;
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");
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);
return ret;
}
-static int create_data_reloc_tree(struct btrfs_trans_handle *trans,
- struct btrfs_root *root)
+static int create_tree(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root, u64 objectid)
{
struct btrfs_key location;
struct btrfs_root_item root_item;
struct extent_buffer *tmp;
- u64 objectid = BTRFS_DATA_RELOC_TREE_OBJECTID;
int ret;
ret = btrfs_copy_root(trans, root, root->node, &tmp, objectid);
filetype = BTRFS_FT_REG_FILE;
if (S_ISLNK(st->st_mode))
filetype = BTRFS_FT_SYMLINK;
+ if (S_ISSOCK(st->st_mode))
+ filetype = BTRFS_FT_SOCK;
+ if (S_ISCHR(st->st_mode))
+ filetype = BTRFS_FT_CHRDEV;
+ if (S_ISBLK(st->st_mode))
+ filetype = BTRFS_FT_BLKDEV;
+ if (S_ISFIFO(st->st_mode))
+ filetype = BTRFS_FT_FIFO;
ret = btrfs_insert_dir_item(trans, root, name, name_len,
parent_inum, &location,
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
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;
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;
* keep our extent size at 1MB max, this makes it easier to work inside
* the tiny block groups created during mkfs
*/
- cur_bytes = min(total_bytes, 1024ULL * 1024);
+ cur_bytes = min(total_bytes, (u64)SZ_1M);
ret = btrfs_reserve_extent(trans, root, cur_bytes, 0, 0, (u64)-1,
&key, 1);
if (ret)
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;
return ret;
}
-static char *make_path(const char *dir, const char *name)
-{
- char *path;
-
- path = malloc(strlen(dir) + strlen(name) + 2);
- if (!path)
- return NULL;
- strcpy(path, dir);
- if (dir[strlen(dir) - 1] != '/')
- strcat(path, "/");
- strcat(path, name);
- return path;
-}
-
static int traverse_directory(struct btrfs_trans_handle *trans,
struct btrfs_root *root, const char *dir_name,
- struct directory_name_entry *dir_head, int out_fd)
+ struct directory_name_entry *dir_head)
{
int ret = 0;
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) {
}
if (S_ISDIR(st.st_mode)) {
+ char tmp[PATH_MAX];
+
dir_entry = malloc(sizeof(struct directory_name_entry));
if (!dir_entry) {
ret = -ENOMEM;
goto fail;
}
dir_entry->dir_name = cur_file->d_name;
- dir_entry->path = make_path(parent_dir_entry->path,
- cur_file->d_name);
+ if (path_cat_out(tmp, parent_dir_entry->path,
+ cur_file->d_name)) {
+ error("invalid path: %s/%s",
+ parent_dir_entry->path,
+ cur_file->d_name);
+ ret = -EINVAL;
+ goto fail;
+ }
+ dir_entry->path = strdup(tmp);
+ if (!dir_entry->path) {
+ error("not enough memory to store path");
+ ret = -ENOMEM;
+ goto fail;
+ }
dir_entry->inum = cur_inum;
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);
u64 size_of_data,
struct mkfs_allocation *allocation)
{
+ struct btrfs_fs_info *fs_info = root->fs_info;
u64 chunk_start;
u64 chunk_size;
u64 meta_type = BTRFS_BLOCK_GROUP_METADATA;
u64 data_type = BTRFS_BLOCK_GROUP_DATA;
- u64 minimum_data_chunk_size = 8 * 1024 * 1024;
+ u64 minimum_data_chunk_size = SZ_8M;
u64 i;
int ret;
for (i = 0; i < num_of_meta_chunks; i++) {
- ret = btrfs_alloc_chunk(trans, 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;
}
-static int make_image(const char *source_dir, struct btrfs_root *root,
- int out_fd)
+static int make_image(const char *source_dir, struct btrfs_root *root)
{
int ret;
struct btrfs_trans_handle *trans;
INIT_LIST_HEAD(&dir_head.list);
trans = btrfs_start_transaction(root, 1);
- ret = traverse_directory(trans, root, source_dir, &dir_head, out_fd);
+ BUG_ON(IS_ERR(trans));
+ ret = traverse_directory(trans, root, source_dir, &dir_head);
if (ret) {
error("unable to traverse directory %s: %d", source_dir, ret);
goto fail;
printf("Making image is completed.\n");
return 0;
fail:
+ /*
+ * Since we don't have btrfs_abort_transaction() yet, uncommitted trans
+ * will trigger a BUG_ON().
+ *
+ * However before mkfs is fully finished, the magic number is invalid,
+ * so even we commit transaction here, the fs still can't be mounted.
+ *
+ * To do a graceful error out, here we commit transaction as a
+ * workaround.
+ * Since we have already hit some problem, the return value doesn't
+ * matter now.
+ */
+ btrfs_commit_transaction(trans, root);
while (!list_empty(&dir_head.list)) {
dir_entry = list_entry(dir_head.list.next,
struct directory_name_entry, list);
u64 dir_size = 0;
u64 total_size = 0;
int ret;
- u64 default_chunk_size = 8 * 1024 * 1024; /* 8MB */
- u64 allocated_meta_size = 8 * 1024 * 1024; /* 8MB */
- u64 allocated_total_size = 20 * 1024 * 1024; /* 20MB */
+ u64 default_chunk_size = SZ_8M;
+ u64 allocated_meta_size = SZ_8M;
+ u64 allocated_total_size = 20 * SZ_1M; /* 20MB */
u64 num_of_meta_chunks = 0;
u64 num_of_data_chunks = 0;
u64 num_of_allocated_meta_chunks =
btrfs_init_path(&path);
trans = btrfs_start_transaction(root, 1);
+ BUG_ON(IS_ERR(trans));
key.objectid = 0;
key.type = BTRFS_BLOCK_GROUP_ITEM_KEY;
ret = btrfs_search_slot(trans, root, &key, &path, 0, 0);
if (ret < 0)
goto out;
+ /* Don't pollute ret for >0 case */
+ if (ret > 0)
+ ret = 0;
btrfs_item_key_to_cpu(path.nodes[0], &found_key,
path.slots[0]);
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;
u32 sectorsize = 4096;
u32 stripesize = 4096;
int zero_end = 1;
- int fd;
+ int fd = -1;
int ret;
+ int close_ret;
int i;
int mixed = 0;
int nodesize_forced = 0;
u64 num_of_meta_chunks = 0;
u64 size_of_data = 0;
u64 source_dir_size = 0;
+ u64 min_dev_size;
int dev_cnt = 0;
int saved_optind;
char fs_uuid[BTRFS_UUID_UNPARSED_SIZE] = { 0 };
error("unrecognized filesystem feature '%s'",
tmp);
free(orig);
- exit(1);
+ goto error;
}
free(orig);
if (features & BTRFS_FEATURE_LIST_ALL) {
btrfs_list_all_fs_features(0);
- exit(0);
+ goto success;
}
break;
}
case 'V':
printf("mkfs.btrfs, part of %s\n",
PACKAGE_STRING);
- exit(0);
- break;
+ goto success;
case 'r':
source_dir = optarg;
source_dir_set = 1;
if (source_dir_set && dev_cnt > 1) {
error("the option -r is limited to a single device");
- exit(1);
+ goto error;
}
if (*fs_uuid) {
if (uuid_parse(fs_uuid, dummy_uuid) != 0) {
error("could not parse UUID: %s", fs_uuid);
- exit(1);
+ goto error;
}
if (!test_uuid_unique(fs_uuid)) {
error("non-unique UUID: %s", fs_uuid);
- exit(1);
+ goto error;
}
}
while (dev_cnt-- > 0) {
file = argv[optind++];
if (is_block_device(file) == 1)
- if (test_dev_for_mkfs(file, force_overwrite))
- exit(1);
+ ret = test_dev_for_mkfs(file, force_overwrite);
+ else
+ ret = test_status_for_mkfs(file, force_overwrite);
+
+ if (ret)
+ goto error;
}
optind = saved_optind;
if (metadata_profile != data_profile) {
error(
"with mixed block groups data and metadata profiles must be the same");
- exit(1);
+ goto error;
}
}
if (btrfs_check_nodesize(nodesize, sectorsize,
features))
- exit(1);
+ goto error;
if (sectorsize < sizeof(struct btrfs_super_block)) {
error("sectorsize smaller than superblock: %u < %zu",
sectorsize, sizeof(struct btrfs_super_block));
- exit(1);
+ goto error;
}
+ min_dev_size = btrfs_min_dev_size(nodesize, mixed, metadata_profile,
+ data_profile);
/* Check device/block_count after the nodesize is determined */
- if (block_count && block_count < btrfs_min_dev_size(nodesize)) {
+ if (block_count && block_count < min_dev_size) {
error("size %llu is too small to make a usable filesystem",
block_count);
error("minimum size for btrfs filesystem is %llu",
- btrfs_min_dev_size(nodesize));
- exit(1);
+ min_dev_size);
+ goto error;
}
for (i = saved_optind; i < saved_optind + dev_cnt; i++) {
char *path;
path = argv[i];
- ret = test_minimum_size(path, nodesize);
+ ret = test_minimum_size(path, min_dev_size);
if (ret < 0) {
error("failed to check size for %s: %s",
path, strerror(-ret));
- exit (1);
+ goto error;
}
if (ret > 0) {
error("'%s' is too small to make a usable filesystem",
path);
error("minimum size for each btrfs device is %llu",
- btrfs_min_dev_size(nodesize));
- exit(1);
+ min_dev_size);
+ goto error;
}
}
ret = test_num_disk_vs_raid(metadata_profile, data_profile,
dev_cnt, mixed, ssd);
if (ret)
- exit(1);
+ goto error;
dev_cnt--;
fd = open(file, O_RDWR);
if (fd < 0) {
error("unable to open %s: %s", file, strerror(errno));
- exit(1);
+ goto error;
}
ret = btrfs_prepare_device(fd, file, &dev_block_count,
block_count,
(discard ? PREP_DEVICE_DISCARD : 0) |
(verbose ? PREP_DEVICE_VERBOSE : 0));
if (ret) {
- close(fd);
- exit(1);
+ goto error;
}
if (block_count && block_count > dev_block_count) {
error("%s is smaller than requested size, expected %llu, found %llu",
file,
(unsigned long long)block_count,
(unsigned long long)dev_block_count);
- exit(1);
+ goto error;
}
} else {
fd = open(file, O_CREAT | O_RDWR,
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
if (fd < 0) {
error("unable to open %s: %s", file, strerror(errno));
- exit(1);
+ goto error;
}
source_dir_size = size_sourcedir(source_dir, sectorsize,
ret = zero_output_file(fd, block_count);
if (ret) {
error("unable to zero the output file");
- exit(1);
+ goto error;
}
/* our "device" is the new image file */
dev_block_count = block_count;
if (dev_block_count < BTRFS_MKFS_SYSTEM_GROUP_SIZE) {
error("device is too small to make filesystem, must be at least %llu",
(unsigned long long)BTRFS_MKFS_SYSTEM_GROUP_SIZE);
- exit(1);
- }
-
- blocks[0] = BTRFS_SUPER_INFO_OFFSET;
- for (i = 1; i < 7; i++) {
- blocks[i] = BTRFS_SUPER_INFO_OFFSET + 1024 * 1024 +
- nodesize * i;
+ goto error;
}
if (group_profile_max_safe_loss(metadata_profile) <
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;
ret = make_btrfs(fd, &mkfs_cfg);
if (ret) {
error("error during mkfs: %s", strerror(-ret));
- exit(1);
+ goto error;
}
fs_info = open_ctree_fs_info(file, 0, 0, 0,
OPEN_CTREE_WRITES | OPEN_CTREE_FS_PARTIAL);
if (!fs_info) {
error("open ctree failed");
- close(fd);
- exit(1);
+ goto error;
}
+ close(fd);
+ fd = -1;
root = fs_info->fs_root;
fs_info->alloc_start = alloc_start;
ret = create_metadata_block_groups(root, mixed, &allocation);
if (ret) {
error("failed to create default block groups: %d", ret);
- exit(1);
+ goto error;
}
trans = btrfs_start_transaction(root, 1);
- if (!trans) {
+ if (IS_ERR(trans)) {
error("failed to start transaction");
- exit(1);
+ goto error;
}
ret = create_data_block_groups(trans, root, mixed, &allocation);
if (ret) {
error("failed to create default data block groups: %d", ret);
- exit(1);
+ goto error;
}
- 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);
+ goto error;
}
ret = btrfs_commit_transaction(trans, root);
}
trans = btrfs_start_transaction(root, 1);
- if (!trans) {
+ if (IS_ERR(trans)) {
error("failed to start transaction");
- exit(1);
+ goto error;
}
if (dev_cnt == 0)
fd = open(file, O_RDWR);
if (fd < 0) {
error("unable to open %s: %s", file, strerror(errno));
- exit(1);
+ goto error;
}
ret = btrfs_device_already_in_root(root, fd,
BTRFS_SUPER_INFO_OFFSET);
(zero_end ? PREP_DEVICE_ZERO_END : 0) |
(discard ? PREP_DEVICE_DISCARD : 0));
if (ret) {
- close(fd);
- exit(1);
+ goto error;
}
ret = btrfs_add_to_fsid(trans, root, fd, file, dev_block_count,
}
}
- ret = create_data_reloc_tree(trans, root);
+ ret = create_tree(trans, root, BTRFS_DATA_RELOC_TREE_OBJECTID);
if (ret) {
error("unable to create data reloc tree: %d", ret);
goto out;
if (source_dir_set) {
trans = btrfs_start_transaction(root, 1);
+ BUG_ON(IS_ERR(trans));
ret = create_chunks(trans, root,
num_of_meta_chunks, size_of_data,
&allocation);
goto out;
}
- ret = make_image(source_dir, root, fd);
+ ret = make_image(source_dir, root);
if (ret) {
error("error wihle filling filesystem: %d", ret);
goto out;
*/
fs_info->finalize_on_close = 1;
out:
- ret = close_ctree(root);
+ close_ret = close_ctree(root);
- if (!ret) {
+ if (!close_ret) {
optind = saved_optind;
dev_cnt = argc - optind;
while (dev_cnt-- > 0) {
free(label);
return !!ret;
+error:
+ if (fd > 0)
+ close(fd);
+
+ free(label);
+ exit(1);
+success:
+ exit(0);
}