btrfs-progs: mute coverity warnings about deadcode
[platform/upstream/btrfs-progs.git] / mkfs.c
diff --git a/mkfs.c b/mkfs.c
index 5ff758f..5940abd 100644 (file)
--- a/mkfs.c
+++ b/mkfs.c
@@ -17,6 +17,7 @@
  */
 
 #include "kerncompat.h"
+#include "androidcompat.h"
 
 #include <sys/ioctl.h>
 #include <sys/mount.h>
@@ -25,7 +26,7 @@
 #include <stdlib.h>
 #include <sys/types.h>
 #include <sys/stat.h>
-#include <sys/dir.h>
+/* #include <sys/dir.h> included via androidcompat.h */
 #include <fcntl.h>
 #include <unistd.h>
 #include <getopt.h>
@@ -59,11 +60,10 @@ struct mkfs_allocation {
        u64 system;
 };
 
-static int make_root_dir(struct btrfs_root *root, int mixed,
+static int create_metadata_block_groups(struct btrfs_root *root, int mixed,
                                struct mkfs_allocation *allocation)
 {
        struct btrfs_trans_handle *trans;
-       struct btrfs_key location;
        u64 bytes_used;
        u64 chunk_start = 0;
        u64 chunk_size = 0;
@@ -117,8 +117,18 @@ static int make_root_dir(struct btrfs_root *root, int mixed,
 
        root->fs_info->system_allocs = 0;
        btrfs_commit_transaction(trans, root);
-       trans = btrfs_start_transaction(root, 1);
-       BUG_ON(!trans);
+
+err:
+       return ret;
+}
+
+static int create_data_block_groups(struct btrfs_trans_handle *trans,
+               struct btrfs_root *root, int mixed,
+               struct mkfs_allocation *allocation)
+{
+       u64 chunk_start = 0;
+       u64 chunk_size = 0;
+       int ret = 0;
 
        if (!mixed) {
                ret = btrfs_alloc_chunk(trans, root->fs_info->extent_root,
@@ -137,6 +147,16 @@ static int make_root_dir(struct btrfs_root *root, int mixed,
                BUG_ON(ret);
        }
 
+err:
+       return ret;
+}
+
+static int make_root_dir(struct btrfs_trans_handle *trans, struct btrfs_root *root,
+               struct mkfs_allocation *allocation)
+{
+       struct btrfs_key location;
+       int ret;
+
        ret = btrfs_make_root_dir(trans, root->fs_info->tree_root,
                              BTRFS_ROOT_TREE_DIR_OBJECTID);
        if (ret)
@@ -159,7 +179,6 @@ static int make_root_dir(struct btrfs_root *root, int mixed,
        if (ret)
                goto err;
 
-       btrfs_commit_transaction(trans, root);
 err:
        return ret;
 }
@@ -229,8 +248,7 @@ static int create_one_raid_group(struct btrfs_trans_handle *trans,
 
 static int create_raid_groups(struct btrfs_trans_handle *trans,
                              struct btrfs_root *root, u64 data_profile,
-                             int data_profile_opt, u64 metadata_profile,
-                             int mixed,
+                             u64 metadata_profile, int mixed,
                              struct mkfs_allocation *allocation)
 {
        u64 num_devices = btrfs_super_num_devices(root->fs_info->super_copy);
@@ -290,9 +308,7 @@ static int create_data_reloc_tree(struct btrfs_trans_handle *trans,
        return 0;
 }
 
-
-static void print_usage(void) __attribute__((noreturn));
-static void print_usage(void)
+static void print_usage(int ret)
 {
        fprintf(stderr, "usage: mkfs.btrfs [options] dev [ dev ... ]\n");
        fprintf(stderr, "options:\n");
@@ -313,7 +329,7 @@ static void print_usage(void)
        fprintf(stderr, "\t-q|--quiet              no messages except errors\n");
        fprintf(stderr, "\t-V|--version            print the mkfs.btrfs version and exit\n");
        fprintf(stderr, "%s\n", PACKAGE_STRING);
-       exit(1);
+       exit(ret);
 }
 
 static void print_version(void) __attribute__((noreturn));
@@ -341,7 +357,7 @@ static u64 parse_profile(char *s)
                return 0;
        } else {
                fprintf(stderr, "Unknown profile %s\n", s);
-               print_usage();
+               exit(1);
        }
        /* not reached */
        return 0;
@@ -585,7 +601,7 @@ static int add_symbolic_link(struct btrfs_trans_handle *trans,
                goto fail;
        }
        if (ret >= sectorsize) {
-               fprintf(stderr, "symlink too long for %s", path_name);
+               fprintf(stderr, "symlink too long for %s\n", path_name);
                ret = -1;
                goto fail;
        }
@@ -652,12 +668,11 @@ static int add_file_items(struct btrfs_trans_handle *trans,
         * do our IO in extent buffers so it can work
         * against any raid type
         */
-       eb = malloc(sizeof(*eb) + sectorsize);
+       eb = calloc(1, sizeof(*eb) + sectorsize);
        if (!eb) {
                ret = -ENOMEM;
                goto end;
        }
-       memset(eb, 0, sizeof(*eb) + sectorsize);
 
 again:
 
@@ -1144,14 +1159,16 @@ static void list_all_devices(struct btrfs_root *root)
        list_for_each_entry(device, &fs_devices->devices, dev_list)
                number_of_devices++;
 
-       printf("  Number of devices:\t%d\n", number_of_devices);
-       printf("    ID   SIZE        PATH\n");
-       printf("    ---  ----------  ------------\n");
+       printf("Number of devices:  %d\n", number_of_devices);
+       /* printf("Total devices size: %10s\n", */
+               /* pretty_size(total_block_count)); */
+       printf("Devices:\n");
+       printf("   ID        SIZE  PATH\n");
        list_for_each_entry_reverse(device, &fs_devices->devices, dev_list) {
                char dev_uuid[BTRFS_UUID_UNPARSED_SIZE];
 
                uuid_unparse(device->uuid, dev_uuid);
-               printf("    %3llu  %10s  %12s\n",
+               printf("  %3llu  %10s  %s\n",
                        device->devid,
                        pretty_size(device->total_bytes),
                        device->name);
@@ -1159,8 +1176,149 @@ static void list_all_devices(struct btrfs_root *root)
        }
 
        printf("\n");
-       printf("  Total devices size: %10s\n",
-               pretty_size(total_block_count));
+}
+
+static int is_temp_block_group(struct extent_buffer *node,
+                              struct btrfs_block_group_item *bgi,
+                              u64 data_profile, u64 meta_profile,
+                              u64 sys_profile)
+{
+       u64 flag = btrfs_disk_block_group_flags(node, bgi);
+       u64 flag_type = flag & BTRFS_BLOCK_GROUP_TYPE_MASK;
+       u64 flag_profile = flag & BTRFS_BLOCK_GROUP_PROFILE_MASK;
+       u64 used = btrfs_disk_block_group_used(node, bgi);
+
+       /*
+        * Chunks meets all the following conditions is a temp chunk
+        * 1) Empty chunk
+        * Temp chunk is always empty.
+        *
+        * 2) profile dismatch with mkfs profile.
+        * Temp chunk is always in SINGLE
+        *
+        * 3) Size differs with mkfs_alloc
+        * Special case for SINGLE/SINGLE btrfs.
+        * In that case, temp data chunk and real data chunk are always empty.
+        * So we need to use mkfs_alloc to be sure which chunk is the newly
+        * allocated.
+        *
+        * Normally, new chunk size is equal to mkfs one (One chunk)
+        * If it has multiple chunks, we just refuse to delete any one.
+        * As they are all single, so no real problem will happen.
+        * So only use condition 1) and 2) to judge them.
+        */
+       if (used != 0)
+               return 0;
+       switch (flag_type) {
+       case BTRFS_BLOCK_GROUP_DATA:
+       case BTRFS_BLOCK_GROUP_DATA | BTRFS_BLOCK_GROUP_METADATA:
+               data_profile &= BTRFS_BLOCK_GROUP_PROFILE_MASK;
+               if (flag_profile != data_profile)
+                       return 1;
+               break;
+       case BTRFS_BLOCK_GROUP_METADATA:
+               meta_profile &= BTRFS_BLOCK_GROUP_PROFILE_MASK;
+               if (flag_profile != meta_profile)
+                       return 1;
+               break;
+       case BTRFS_BLOCK_GROUP_SYSTEM:
+               sys_profile &= BTRFS_BLOCK_GROUP_PROFILE_MASK;
+               if (flag_profile != sys_profile)
+                       return 1;
+               break;
+       }
+       return 0;
+}
+
+/* Note: if current is a block group, it will skip it anyway */
+static int next_block_group(struct btrfs_root *root,
+                           struct btrfs_path *path)
+{
+       struct btrfs_key key;
+       int ret = 0;
+
+       while (1) {
+               ret = btrfs_next_item(root, path);
+               if (ret)
+                       goto out;
+
+               btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
+               if (key.type == BTRFS_BLOCK_GROUP_ITEM_KEY)
+                       goto out;
+       }
+out:
+       return ret;
+}
+
+/* This function will cleanup  */
+static int cleanup_temp_chunks(struct btrfs_fs_info *fs_info,
+                              struct mkfs_allocation *alloc,
+                              u64 data_profile, u64 meta_profile,
+                              u64 sys_profile)
+{
+       struct btrfs_trans_handle *trans = NULL;
+       struct btrfs_block_group_item *bgi;
+       struct btrfs_root *root = fs_info->extent_root;
+       struct btrfs_key key;
+       struct btrfs_key found_key;
+       struct btrfs_path *path;
+       int ret = 0;
+
+       path = btrfs_alloc_path();
+       if (!path) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       trans = btrfs_start_transaction(root, 1);
+
+       key.objectid = 0;
+       key.type = BTRFS_BLOCK_GROUP_ITEM_KEY;
+       key.offset = 0;
+
+       while (1) {
+               /*
+                * as the rest of the loop may modify the tree, we need to
+                * start a new search each time.
+                */
+               ret = btrfs_search_slot(trans, root, &key, path, 0, 0);
+               if (ret < 0)
+                       goto out;
+
+               btrfs_item_key_to_cpu(path->nodes[0], &found_key,
+                                     path->slots[0]);
+               if (found_key.objectid < key.objectid)
+                       goto out;
+               if (found_key.type != BTRFS_BLOCK_GROUP_ITEM_KEY) {
+                       ret = next_block_group(root, path);
+                       if (ret < 0)
+                               goto out;
+                       if (ret > 0) {
+                               ret = 0;
+                               goto out;
+                       }
+                       btrfs_item_key_to_cpu(path->nodes[0], &found_key,
+                                             path->slots[0]);
+               }
+
+               bgi = btrfs_item_ptr(path->nodes[0], path->slots[0],
+                                    struct btrfs_block_group_item);
+               if (is_temp_block_group(path->nodes[0], bgi,
+                                       data_profile, meta_profile,
+                                       sys_profile)) {
+                       ret = btrfs_free_block_group(trans, fs_info,
+                                       found_key.objectid, found_key.offset);
+                       if (ret < 0)
+                               goto out;
+               }
+               btrfs_release_path(path);
+               key.objectid = found_key.objectid + found_key.offset;
+       }
+out:
+       if (trans)
+               btrfs_commit_transaction(trans, root);
+       btrfs_free_path(path);
+       return ret;
 }
 
 int main(int ac, char **av)
@@ -1190,7 +1348,6 @@ int main(int ac, char **av)
        int discard = 1;
        int ssd = 0;
        int force_overwrite = 0;
-
        char *source_dir = NULL;
        int source_dir_set = 0;
        u64 num_of_meta_chunks = 0;
@@ -1198,10 +1355,10 @@ int main(int ac, char **av)
        u64 source_dir_size = 0;
        int dev_cnt = 0;
        int saved_optind;
-       char estr[100];
        char fs_uuid[BTRFS_UUID_UNPARSED_SIZE] = { 0 };
        u64 features = BTRFS_MKFS_DEFAULT_FEATURES;
        struct mkfs_allocation allocation = { 0 };
+       struct btrfs_mkfs_config mkfs_cfg;
 
        while(1) {
                int c;
@@ -1222,6 +1379,7 @@ int main(int ac, char **av)
                        { "features", required_argument, NULL, 'O' },
                        { "uuid", required_argument, NULL, 'U' },
                        { "quiet", 0, NULL, 'q' },
+                       { "help", no_argument, NULL, GETOPT_VAL_HELP },
                        { NULL, 0, NULL, 0}
                };
 
@@ -1281,8 +1439,6 @@ int main(int ac, char **av)
                                break;
                        case 'b':
                                block_count = parse_size(optarg);
-                               if (block_count <= BTRFS_MKFS_SMALL_VOLUME_SIZE)
-                                       mixed = 1;
                                zero_end = 0;
                                break;
                        case 'V':
@@ -1302,17 +1458,17 @@ int main(int ac, char **av)
                        case 'q':
                                verbose = 0;
                                break;
+                       case GETOPT_VAL_HELP:
                        default:
-                               print_usage();
+                               print_usage(c != GETOPT_VAL_HELP);
                }
        }
+
        sectorsize = max(sectorsize, (u32)sysconf(_SC_PAGESIZE));
-       if (btrfs_check_nodesize(nodesize, sectorsize))
-               exit(1);
        saved_optind = optind;
        dev_cnt = ac - optind;
        if (dev_cnt == 0)
-               print_usage();
+               print_usage(1);
 
        if (source_dir_set && dev_cnt > 1) {
                fprintf(stderr,
@@ -1332,14 +1488,12 @@ int main(int ac, char **av)
                        exit(1);
                }
        }
-       
+
        while (dev_cnt-- > 0) {
                file = av[optind++];
-               if (is_block_device(file))
-                       if (test_dev_for_mkfs(file, force_overwrite, estr)) {
-                               fprintf(stderr, "Error: %s", estr);
+               if (is_block_device(file) == 1)
+                       if (test_dev_for_mkfs(file, force_overwrite))
                                exit(1);
-                       }
        }
 
        optind = saved_optind;
@@ -1348,10 +1502,9 @@ int main(int ac, char **av)
        file = av[optind++];
        ssd = is_ssd(file);
 
-       if (is_vol_small(file) || mixed) {
+       if (mixed) {
                if (verbose)
-                       printf("SMALL VOLUME: forcing mixed metadata/data groups\n");
-               mixed = 1;
+                       printf("Forcing mixed metadata/data groups\n");
        }
 
        /*
@@ -1384,17 +1537,12 @@ int main(int ac, char **av)
                        }
                }
 
-               if (!nodesize_forced) {
+               if (!nodesize_forced)
                        nodesize = best_nodesize;
-                       if (btrfs_check_nodesize(nodesize, sectorsize))
-                               exit(1);
-               }
-               if (nodesize != sectorsize) {
-                       fprintf(stderr, "Error: mixed metadata/data block groups "
-                               "require metadata blocksizes equal to the sectorsize\n");
-                       exit(1);
-               }
        }
+       if (btrfs_check_nodesize(nodesize, sectorsize,
+                                features))
+               exit(1);
 
        /* Check device/block_count after the nodesize is determined */
        if (block_count && block_count < btrfs_min_dev_size(nodesize)) {
@@ -1427,11 +1575,9 @@ int main(int ac, char **av)
                }
        }
        ret = test_num_disk_vs_raid(metadata_profile, data_profile,
-                       dev_cnt, mixed, estr);
-       if (ret) {
-               fprintf(stderr, "Error: %s\n", estr);
+                       dev_cnt, mixed);
+       if (ret)
                exit(1);
-       }
 
        /* if we are here that means all devs are good to btrfsify */
        if (verbose) {
@@ -1454,7 +1600,7 @@ int main(int ac, char **av)
                        exit(1);
                }
                ret = btrfs_prepare_device(fd, file, zero_end, &dev_block_count,
-                                          block_count, &mixed, discard);
+                                          block_count, discard);
                if (ret) {
                        close(fd);
                        exit(1);
@@ -1513,8 +1659,16 @@ int main(int ac, char **av)
                features |= BTRFS_FEATURE_INCOMPAT_RAID56;
        }
 
-       ret = make_btrfs(fd, file, label, fs_uuid, blocks, dev_block_count,
-                        nodesize, sectorsize, stripesize, features);
+       mkfs_cfg.label = label;
+       mkfs_cfg.fs_uuid = fs_uuid;
+       memcpy(mkfs_cfg.blocks, blocks, sizeof(blocks));
+       mkfs_cfg.num_bytes = dev_block_count;
+       mkfs_cfg.nodesize = nodesize;
+       mkfs_cfg.sectorsize = sectorsize;
+       mkfs_cfg.stripesize = stripesize;
+       mkfs_cfg.features = features;
+
+       ret = make_btrfs(fd, &mkfs_cfg);
        if (ret) {
                fprintf(stderr, "error during mkfs: %s\n", strerror(-ret));
                exit(1);
@@ -1528,23 +1682,45 @@ int main(int ac, char **av)
        }
        root->fs_info->alloc_start = alloc_start;
 
-       ret = make_root_dir(root, mixed, &allocation);
+       ret = create_metadata_block_groups(root, mixed, &allocation);
+       if (ret) {
+               fprintf(stderr, "failed to create default block groups\n");
+               exit(1);
+       }
+
+       trans = btrfs_start_transaction(root, 1);
+       if (!trans) {
+               fprintf(stderr, "failed to start transaction\n");
+               exit(1);
+       }
+
+       ret = create_data_block_groups(trans, root, mixed, &allocation);
+       if (ret) {
+               fprintf(stderr, "failed to create default data block groups\n");
+               exit(1);
+       }
+
+       ret = make_root_dir(trans, root, &allocation);
        if (ret) {
                fprintf(stderr, "failed to setup the root directory\n");
                exit(1);
        }
 
+       btrfs_commit_transaction(trans, root);
+
        trans = btrfs_start_transaction(root, 1);
+       if (!trans) {
+               fprintf(stderr, "failed to start transaction\n");
+               exit(1);
+       }
 
-       if (is_block_device(file))
+       if (is_block_device(file) == 1)
                btrfs_register_one_device(file);
 
        if (dev_cnt == 0)
                goto raid_groups;
 
        while (dev_cnt-- > 0) {
-               int old_mixed = mixed;
-
                file = av[optind++];
 
                /*
@@ -1567,12 +1743,11 @@ int main(int ac, char **av)
                        continue;
                }
                ret = btrfs_prepare_device(fd, file, zero_end, &dev_block_count,
-                                          block_count, &mixed, discard);
+                                          block_count, discard);
                if (ret) {
                        close(fd);
                        exit(1);
                }
-               mixed = old_mixed;
 
                ret = btrfs_add_to_fsid(trans, root, fd, file, dev_block_count,
                                        sectorsize, sectorsize, sectorsize);
@@ -1586,15 +1761,14 @@ int main(int ac, char **av)
                                (unsigned long long)device->devid);
                }
 
-               if (is_block_device(file))
+               if (is_block_device(file) == 1)
                        btrfs_register_one_device(file);
        }
 
 raid_groups:
        if (!source_dir_set) {
                ret = create_raid_groups(trans, root, data_profile,
-                                data_profile_opt, metadata_profile,
-                                mixed, &allocation);
+                                metadata_profile, mixed, &allocation);
                BUG_ON(ret);
        }
 
@@ -1614,44 +1788,50 @@ raid_groups:
                ret = make_image(source_dir, root, fd);
                BUG_ON(ret);
        }
+       ret = cleanup_temp_chunks(root->fs_info, &allocation, data_profile,
+                                 metadata_profile, metadata_profile);
+       if (ret < 0) {
+               fprintf(stderr, "Failed to cleanup temporary chunks\n");
+               goto out;
+       }
 
        if (verbose) {
                char features_buf[64];
 
-               printf("BTRFS filesystem summary:\n");
-               printf("  Label:\t\t%s\n", label);
-               printf("  UUID:\t\t\t%s\n", fs_uuid);
-               printf("\n");
-
-               printf("  Node size:\t\t%u\n", nodesize);
-               printf("  Sector size:\t\t%u\n", sectorsize);
-               printf("  Initial chunks:\n");
+               printf("Label:              %s\n", label);
+               printf("UUID:               %s\n", fs_uuid);
+               printf("Node size:          %u\n", nodesize);
+               printf("Sector size:        %u\n", sectorsize);
+               printf("Filesystem size:    %s\n",
+                       pretty_size(btrfs_super_total_bytes(root->fs_info->super_copy)));
+               printf("Block group profiles:\n");
                if (allocation.data)
-                       printf("    Data:\t\t%s\n",
+                       printf("  Data:             %-8s %16s\n",
+                               btrfs_group_profile_str(data_profile),
                                pretty_size(allocation.data));
                if (allocation.metadata)
-                       printf("    Metadata:\t\t%s\n",
+                       printf("  Metadata:         %-8s %16s\n",
+                               btrfs_group_profile_str(metadata_profile),
                                pretty_size(allocation.metadata));
                if (allocation.mixed)
-                       printf("    Data+Metadata:\t%s\n",
+                       printf("  Data+Metadata:    %-8s %16s\n",
+                               btrfs_group_profile_str(data_profile),
                                pretty_size(allocation.mixed));
-               printf("    System:\t\t%s\n",
+               printf("  System:           %-8s %16s\n",
+                       btrfs_group_profile_str(metadata_profile),
                        pretty_size(allocation.system));
-               printf("  Metadata profile:\t%s\n",
-                       btrfs_group_profile_str(metadata_profile));
-               printf("  Data profile:\t\t%s\n",
-                       btrfs_group_profile_str(data_profile));
-               printf("  Mixed mode:\t\t%s\n", mixed ? "YES" : "NO");
-               printf("  SSD detected:\t\t%s\n", ssd ? "YES" : "NO");
+               printf("SSD detected:       %s\n", ssd ? "yes" : "no");
                btrfs_parse_features_to_string(features_buf, features);
-               printf("  Incompat features:\t%s", features_buf);
+               printf("Incompat features:  %s", features_buf);
                printf("\n");
 
                list_all_devices(root);
        }
 
+out:
        ret = close_ctree(root);
        BUG_ON(ret);
+       btrfs_close_all_devices();
        free(label);
        return 0;
 }