btrfs-progs: mkfs: Enhance minimal device size calculation to fix mkfs failure on...
[platform/upstream/btrfs-progs.git] / mkfs / main.c
index dfa7a03..90fab59 100644 (file)
@@ -47,6 +47,8 @@
 #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;
 
@@ -75,6 +77,7 @@ static int create_metadata_block_groups(struct btrfs_root *root, int mixed,
        int ret;
 
        trans = btrfs_start_transaction(root, 1);
+       BUG_ON(IS_ERR(trans));
        bytes_used = btrfs_super_bytes_used(fs_info->super_copy);
 
        root->fs_info->system_allocs = 1;
@@ -320,13 +323,12 @@ static int create_raid_groups(struct btrfs_trans_handle *trans,
        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);
@@ -435,6 +437,14 @@ static int add_directory_items(struct btrfs_trans_handle *trans,
                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,
@@ -785,20 +795,6 @@ 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)
@@ -933,14 +929,28 @@ static int traverse_directory(struct btrfs_trans_handle *trans,
                        }
 
                        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)) {
@@ -1047,6 +1057,7 @@ static int make_image(const char *source_dir, struct btrfs_root *root)
        INIT_LIST_HEAD(&dir_head.list);
 
        trans = btrfs_start_transaction(root, 1);
+       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);
@@ -1062,6 +1073,19 @@ static int make_image(const char *source_dir, struct btrfs_root *root)
                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);
@@ -1325,6 +1349,7 @@ static int cleanup_temp_chunks(struct btrfs_fs_info *fs_info,
 
        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;
@@ -1338,6 +1363,9 @@ static int cleanup_temp_chunks(struct btrfs_fs_info *fs_info,
                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]);
@@ -1411,6 +1439,7 @@ int main(int argc, char **argv)
        int zero_end = 1;
        int fd = -1;
        int ret;
+       int close_ret;
        int i;
        int mixed = 0;
        int nodesize_forced = 0;
@@ -1424,6 +1453,7 @@ int main(int argc, char **argv)
        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 };
@@ -1568,8 +1598,12 @@ int main(int argc, char **argv)
        while (dev_cnt-- > 0) {
                file = argv[optind++];
                if (is_block_device(file) == 1)
-                       if (test_dev_for_mkfs(file, force_overwrite))
-                               goto error;
+                       ret = test_dev_for_mkfs(file, force_overwrite);
+               else
+                       ret = test_status_for_mkfs(file, force_overwrite);
+
+               if (ret)
+                       goto error;
        }
 
        optind = saved_optind;
@@ -1634,19 +1668,21 @@ int main(int argc, char **argv)
                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));
+                       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));
@@ -1656,7 +1692,7 @@ int main(int argc, char **argv)
                        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));
+                               min_dev_size);
                        goto error;
                }
        }
@@ -1758,7 +1794,7 @@ int main(int argc, char **argv)
        }
 
        trans = btrfs_start_transaction(root, 1);
-       if (!trans) {
+       if (IS_ERR(trans)) {
                error("failed to start transaction");
                goto error;
        }
@@ -1782,7 +1818,7 @@ int main(int argc, char **argv)
        }
 
        trans = btrfs_start_transaction(root, 1);
-       if (!trans) {
+       if (IS_ERR(trans)) {
                error("failed to start transaction");
                goto error;
        }
@@ -1846,7 +1882,7 @@ raid_groups:
                }
        }
 
-       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;
@@ -1860,6 +1896,7 @@ raid_groups:
 
        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);
@@ -1925,9 +1962,9 @@ raid_groups:
         */
        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) {