[f2fs] Enable mkfs options for f2fs

This change enables to configure mkfs options as below.

  -l label
  -a heap-based allocation [default: 1]
  -o overprovision ratio [default: 5]
  -s # of segments per section [default: 1]
  -z # of sections per zone [default: 1]
  -e [extension list] e.g. "mp3,gif,mov"

Test: fx test f2fs-unittest fs-tests large-fs-tests

Prerequisite:
FUCHSIA_DIR$ patch -p1 < \
third_party/f2fs/patches/0007-f2fs-Add-filesystem-specific-mkfs-option.patch

Change-Id: Ibd944e9b4638f0ba122572355dd2e5eca3520094
Reviewed-on: https://fuchsia-review.googlesource.com/c/third_party/f2fs/+/543302
Reviewed-by: Brett Wilson <brettw@google.com>
diff --git a/f2fs.cc b/f2fs.cc
index b120c87..40f31df 100644
--- a/f2fs.cc
+++ b/f2fs.cc
@@ -56,12 +56,6 @@
                               kBlockSize);
 }
 
-zx_status_t Mkfs(const MkfsOptions& options, std::unique_ptr<f2fs::Bcache> bc) {
-  F2fsMkfs mkfs(std::move(bc), options);
-
-  return mkfs.Mkfs();
-}
-
 zx_status_t Fsck(const MountOptions& options, std::unique_ptr<f2fs::Bcache> bc) { return ZX_OK; }
 
 F2fs::F2fs(std::unique_ptr<f2fs::Bcache> bc, SuperBlock* sb, const MountOptions& mount_options)
diff --git a/f2fs.h b/f2fs.h
index c8edf22..fcdf6d2 100644
--- a/f2fs.h
+++ b/f2fs.h
@@ -65,12 +65,12 @@
   kExportDirectory
 };
 
-zx_status_t Mkfs(const MkfsOptions &options, std::unique_ptr<f2fs::Bcache> bc);
 zx_status_t Fsck(const MountOptions &options, std::unique_ptr<f2fs::Bcache> bc);
 zx_status_t Mount(const MountOptions &options, std::unique_ptr<f2fs::Bcache> bc);
 
 zx_status_t CreateBcache(std::unique_ptr<block_client::BlockDevice> device, bool *out_readonly,
                          std::unique_ptr<f2fs::Bcache> *out);
+zx_status_t LoadSuperblock(f2fs::Bcache *bc, SuperBlock *out_info);
 
 using SyncCallback = fs::Vnode::SyncCallback;
 
diff --git a/file.cc b/file.cc
index 98029f5..654f3d3 100644
--- a/file.cc
+++ b/file.cc
@@ -399,8 +399,8 @@
       // If it fails with any reasons such as no space/memory,
       // every data_pages[less than index] is released, and DoWrite() returns with the err code.
       for (uint64_t m = 0; m < index; m++) {
-          if (data_pages[m] != nullptr)
-            F2fsPutPage(data_pages[m], 1);
+        if (data_pages[m] != nullptr)
+          F2fsPutPage(data_pages[m], 1);
       }
 
       *out_actual = 0;
diff --git a/mkfs.cc b/mkfs.cc
index 8fcb2b8..8c99f7f 100644
--- a/mkfs.cc
+++ b/mkfs.cc
@@ -2,45 +2,117 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <getopt.h>
+
+#include <cmath>
 #include <codecvt>
 
 #include "f2fs.h"
 #include "src/lib/uuid/uuid.h"
+#include <lib/syslog/cpp/macros.h>
 
 namespace f2fs {
 
-const char *kMediaExtList[] = {"jpg", "gif", "png",  "avi", "divx", "mp4", "mp3", "3gp",
-                               "wmv", "wma", "mpeg", "mkv", "mov",  "asx", "asf", "wmx",
-                               "svi", "wvx", "wm",   "mpg", "mpe",  "rm",  "ogg"};
+MkfsWorker::MkfsWorker(Bcache *bc) : bc_(bc) {}
 
-F2fsMkfs::F2fsMkfs(std::unique_ptr<f2fs::Bcache> bc, const MkfsOptions &mkfs_options)
-    : bc_(std::move(bc)), mkfs_options_(mkfs_options) {}
+void MkfsWorker::PrintUsage() {
+  fprintf(stderr, "Usage: mkfs -p \"[OPTIONS]\" devicepath f2fs\n");
+  fprintf(stderr, "[OPTIONS]\n");
+  fprintf(stderr, "  -l label\n");
+  fprintf(stderr, "  -a heap-based allocation [default: 1]\n");
+  fprintf(stderr, "  -o overprovision ratio [default: 5]\n");
+  fprintf(stderr, "  -s # of segments per section [default: 1]\n");
+  fprintf(stderr, "  -z # of sections per zone [default: 1]\n");
+  fprintf(stderr, "  -e [extension list] e.g. \"mp3,gif,mov\"\n");
+  fprintf(stderr, "e.g. mkfs -p \"-l hello -a 1 -o 5 -s 1 -z 1 -e mp3,gif\" devicepath f2fs\n");
+}
 
-zx_status_t F2fsMkfs::Mkfs() {
-  char extension_list[] = "";
+zx_status_t MkfsWorker::ParseOptions(int argc, char **argv) {
+  struct option opts[] = {
+      {"label", required_argument, nullptr, 'l'},
+      {"heap", required_argument, nullptr, 'a'},
+      {"op", required_argument, nullptr, 'o'},
+      {"seg_per_sec", required_argument, nullptr, 's'},
+      {"sec_per_zone", required_argument, nullptr, 'z'},
+      {"ext_list", required_argument, nullptr, 'e'},
+      {nullptr, 0, nullptr, 0},
+  };
 
+  int opt_index = -1;
+  int c = -1;
+
+  optreset = 1;
+  optind = 1;
+
+  while ((c = getopt_long(argc, argv, "l:a:o:s:z:e:", opts, &opt_index)) >= 0) {
+    switch (c) {
+      case 'l':
+        mkfs_options_.label = optarg;
+        if (strlen(mkfs_options_.label) >= sizeof(params_.vol_label)) {
+          fprintf(stderr, "ERROR: label length should be less than 16.\n");
+          return ZX_ERR_INVALID_ARGS;
+        }
+        break;
+      case 'a':
+        mkfs_options_.heap_based_allocation =
+            (static_cast<uint32_t>(strtoul(optarg, NULL, 0)) != 0);
+        break;
+      case 'o':
+        mkfs_options_.overprovision_ratio = static_cast<uint32_t>(strtoul(optarg, NULL, 0));
+        if (mkfs_options_.overprovision_ratio == 0) {
+          fprintf(stderr, "ERROR: overprovision ratio should be larger than 0.\n");
+          return ZX_ERR_INVALID_ARGS;
+        }
+        break;
+      case 's':
+        mkfs_options_.segs_per_sec = static_cast<uint32_t>(strtoul(optarg, NULL, 0));
+        if (mkfs_options_.segs_per_sec == 0) {
+          fprintf(stderr, "ERROR: # of segments per section should be larger than 0.\n");
+          return ZX_ERR_INVALID_ARGS;
+        }
+        break;
+      case 'z':
+        mkfs_options_.secs_per_zone = static_cast<uint32_t>(strtoul(optarg, NULL, 0));
+        if (mkfs_options_.secs_per_zone == 0) {
+          fprintf(stderr, "ERROR: # of sections per zone should be larger than 0.\n");
+          return ZX_ERR_INVALID_ARGS;
+        }
+        break;
+      case 'e':
+        mkfs_options_.extension_list = optarg;
+        break;
+      default:
+        PrintUsage();
+        return ZX_ERR_INVALID_ARGS;
+    };
+  };
+
+  return ZX_OK;
+}
+
+void MkfsWorker::PrintCurrentOption() {
+  fprintf(stderr, "f2fs mkfs label = %s\n", mkfs_options_.label);
+  fprintf(stderr, "f2fs mkfs heap-based allocation = %d\n", mkfs_options_.heap_based_allocation);
+  fprintf(stderr, "f2fs mkfs overprovision ratio = %u\n", mkfs_options_.overprovision_ratio);
+  fprintf(stderr, "f2fs mkfs segments per sector = %u\n", mkfs_options_.segs_per_sec);
+  fprintf(stderr, "f2fs mkfs sectors per zone = %u\n", mkfs_options_.secs_per_zone);
+  fprintf(stderr, "f2fs mkfs extension list = %s\n", mkfs_options_.extension_list);
+}
+
+zx_status_t MkfsWorker::DoMkfs() {
 #ifdef F2FS_BU_DEBUG
-  // TODO: lable, extention list
-  std::cout << "f2fs mkfs heap-based allocation = " << mkfs_options_.heap_based_allocation
-            << std::endl;
-  std::cout << "f2fs mkfs overprovision ratio = " << mkfs_options_.overprovision_ratio << std::endl;
-  std::cout << "f2fs mkfs # of segments per section = " << mkfs_options_.num_of_seg_per_sec
-            << std::endl;
-  std::cout << "f2fs mkfs overprovision ratio = " << mkfs_options_.num_of_sec_per_zone << std::endl;
+  PrintCurrentOption();
 #endif
 
-  // TODO: parse mkfs options
-  params_.extension_list = extension_list;
-
   InitGlobalParameters();
 
-  if (GetDeviceInfo() < 0)
-    return -1;
+  if (zx_status_t ret = GetDeviceInfo(); ret != ZX_OK)
+    return ret;
 
-  if (FormatDevice() < 0)
-    return -1;
+  if (zx_status_t ret = FormatDevice(); ret != ZX_OK)
+    return ret;
 
-  printf("Info: format successful\n");
+  fprintf(stderr, "Info: format successful\n");
 
   return ZX_OK;
 }
@@ -48,74 +120,40 @@
 /*
  * String must be less than 16 characters.
  */
-void F2fsMkfs::AsciiToUnicode(const std::string &in_string, std::u16string *out_string) {
+void AsciiToUnicode(const std::string &in_string, std::u16string *out_string) {
   std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> cvt16;
 
   out_string->assign(cvt16.from_bytes(in_string));
 }
 
-void F2fsMkfs::InitGlobalParameters() {
+void MkfsWorker::InitGlobalParameters() {
   static_assert(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__);
 
   params_.sector_size = kDefaultSectorSize;
   params_.sectors_per_blk = kDefaultSectorsPerBlock;
   params_.blks_per_seg = kDefaultBlocksPerSegment;
   params_.reserved_segments = 20; /* calculated by overprovision ratio */
-  params_.overprovision = 5;
-  params_.segs_per_sec = 1;
-  params_.secs_per_zone = 1;
-  params_.heap = 1;
-  memset(params_.vol_label, 0, sizeof(params_.vol_label));
+  params_.overprovision = mkfs_options_.overprovision_ratio;
+  params_.segs_per_sec = mkfs_options_.segs_per_sec;
+  params_.secs_per_zone = mkfs_options_.secs_per_zone;
+  params_.heap = (mkfs_options_.heap_based_allocation ? 1 : 0);
+  if (mkfs_options_.label != nullptr) {
+    memcpy(params_.vol_label, mkfs_options_.label, strlen(mkfs_options_.label) + 1);
+  } else {
+    memset(params_.vol_label, 0, sizeof(params_.vol_label));
 
-  params_.vol_label[0] = 'F';
-  params_.vol_label[1] = '2';
-  params_.vol_label[2] = 'F';
-  params_.vol_label[3] = 'S';
-  params_.vol_label[4] = '\0';
+    params_.vol_label[0] = 'F';
+    params_.vol_label[1] = '2';
+    params_.vol_label[2] = 'F';
+    params_.vol_label[3] = 'S';
+    params_.vol_label[4] = '\0';
+  }
   params_.device_name = nullptr;
+
+  params_.extension_list = mkfs_options_.extension_list;
 }
 
-inline int F2fsMkfs::F2fsSetBit(uint32_t nr, uint8_t *addr) {
-  int mask;
-  int ret;
-
-  addr += (nr >> 3);
-  mask = 1 << (7 - (nr & 0x07));
-  ret = mask & *addr;
-  *addr |= mask;
-  return ret;
-}
-
-int8_t F2fsMkfs::LogBase2(uint32_t num) {
-  int8_t ret = 0;
-
-  if (num <= 0 || (num & (num - 1)) != 0) {
-    return -1;
-  }
-
-  while (num >>= 1) {
-    ret++;
-  }
-
-  return ret;
-}
-
-#if 0  // porting needed
-void F2fsMkfs::f2fs_usage(void)
-{
-        fprintf(stderr, "Usage: f2fs_format [options] device\n");
-        fprintf(stderr, "[options]\n");
-        fprintf(stderr, "-l label\n");
-        fprintf(stderr, "-a heap-based allocation [default:1]\n");
-        fprintf(stderr, "-o overprovision ratio [default:5]\n");
-        fprintf(stderr, "-s # of segments per section [default:1]\n");
-        fprintf(stderr, "-z # of sections per zone [default:1]\n");
-        fprintf(stderr, "-e [extension list] e.g. \"mp3,gif,mov\"\n");
-        exit(1);
-}
-#endif
-
-zx_status_t F2fsMkfs::GetDeviceInfo() {
+zx_status_t MkfsWorker::GetDeviceInfo() {
   fuchsia_hardware_block_BlockInfo info;
 
   bc_->device()->BlockGetInfo(&info);
@@ -128,52 +166,45 @@
   params_.start_sector = kSuperblockStart;
 
   if (params_.total_sectors < (kMinVolumeSize / kDefaultSectorSize)) {
-    printf("Error: Min volume size supported is %d\n", kMinVolumeSize);
+    fprintf(stderr, "Error: Min volume size supported is %d\n", kMinVolumeSize);
     return ZX_ERR_NO_RESOURCES;
   }
 
   return ZX_OK;
 }
 
-void F2fsMkfs::ConfigureExtensionList() {
+void MkfsWorker::ConfigureExtensionList() {
   char *ext_str = params_.extension_list;
-  char *ue;
-  int name_len;
-  int i = 0;
 
   super_block_.extension_count = 0;
   memset(super_block_.extension_list, 0, sizeof(super_block_.extension_list));
 
+  int name_len;
+  int i = 0;
+
   for (const char *ext : kMediaExtList) {
     name_len = static_cast<int>(strlen(ext));
     memcpy(super_block_.extension_list[i++], ext, name_len);
   }
-  super_block_.extension_count = i - 1;
+  super_block_.extension_count = i;
 
   if (!ext_str)
     return;
 
   /* add user ext list */
-  ue = strtok(ext_str, ",");
+  char *ue = strtok(ext_str, ",");
   while (ue != nullptr) {
     name_len = static_cast<int>(strlen(ue));
     memcpy(super_block_.extension_list[i++], ue, name_len);
     ue = strtok(nullptr, ",");
-    if (i > kMaxExtension)
+    if (i >= kMaxExtension)
       break;
   }
 
-  super_block_.extension_count = i - 1;
-#if 0  // porting needed
-  // TODO: strdup in f2fs_parse_options
-  // free(params_.extension_list);
-#endif
+  super_block_.extension_count = i;
 }
 
-zx_status_t F2fsMkfs::WriteToDisk(void *buf, uint64_t offset, size_t length) {
-  zx_status_t status = 0;
-  uint64_t curr_offset = offset;
-
+zx_status_t MkfsWorker::WriteToDisk(void *buf, uint64_t offset, size_t length) {
 #ifdef F2FS_BU_DEBUG
   std::cout << std::hex << "writetodeisk: offset= 0x" << offset << " length= 0x" << length
             << std::endl;
@@ -191,6 +222,9 @@
     return ZX_ERR_INVALID_ARGS;
   }
 
+  zx_status_t status = ZX_OK;
+  uint64_t curr_offset = offset;
+
   for (uint64_t i = 0; i < length / kBlockSize; i++) {
     if ((status = bc_->Writeblk((offset / kBlockSize) + i, buf)) != ZX_OK) {
       std::cout << "mkfs: Failed to write root directory: " << status << std::endl;
@@ -204,26 +238,17 @@
   return status;
 }
 
-zx_status_t F2fsMkfs::PrepareSuperBlock() {
-  uint32_t blk_size_bytes;
-  uint32_t log_sectorsize, log_sectors_per_block;
-  uint32_t log_blocksize, log_blks_per_seg;
-  uint32_t segment_size_bytes, zone_size_bytes;
-  uint32_t sit_segments;
-  uint32_t blocks_for_sit, blocks_for_nat, blocks_for_ssa;
-  uint32_t total_valid_blks_available;
-  uint64_t zone_align_start_offset, diff, total_meta_segments;
-  uint32_t sit_bitmap_size, max_nat_bitmap_size, max_nat_segments;
-  uint32_t total_zones;
-
+zx_status_t MkfsWorker::PrepareSuperBlock() {
   super_block_.magic = CpuToLe(uint32_t{kF2fsSuperMagic});
   super_block_.major_ver = CpuToLe(kMajorVersion);
   super_block_.minor_ver = CpuToLe(kMinorVersion);
 
-  log_sectorsize = LogBase2(params_.sector_size);
-  log_sectors_per_block = LogBase2(params_.sectors_per_blk);
-  log_blocksize = log_sectorsize + log_sectors_per_block;
-  log_blks_per_seg = LogBase2(params_.blks_per_seg);
+  uint32_t log_sectorsize = static_cast<uint32_t>(log2(static_cast<double>(params_.sector_size)));
+  uint32_t log_sectors_per_block =
+      static_cast<uint32_t>(log2(static_cast<double>(params_.sectors_per_blk)));
+  uint32_t log_blocksize = log_sectorsize + log_sectors_per_block;
+  uint32_t log_blks_per_seg =
+      static_cast<uint32_t>(log2(static_cast<double>(params_.blks_per_seg)));
 
   super_block_.log_sectorsize = CpuToLe(log_sectorsize);
 
@@ -249,16 +274,16 @@
 
   super_block_.segs_per_sec = CpuToLe(params_.segs_per_sec);
   super_block_.secs_per_zone = CpuToLe(params_.secs_per_zone);
-  blk_size_bytes = 1 << log_blocksize;
-  segment_size_bytes = blk_size_bytes * params_.blks_per_seg;
-  zone_size_bytes =
+  uint32_t blk_size_bytes = 1 << log_blocksize;
+  uint32_t segment_size_bytes = blk_size_bytes * params_.blks_per_seg;
+  uint32_t zone_size_bytes =
       blk_size_bytes * params_.secs_per_zone * params_.segs_per_sec * params_.blks_per_seg;
 
   super_block_.checksum_offset = 0;
 
   super_block_.block_count = CpuToLe((params_.total_sectors * kDefaultSectorSize) / blk_size_bytes);
 
-  zone_align_start_offset =
+  uint64_t zone_align_start_offset =
       (params_.start_sector * kDefaultSectorSize + 2 * kBlkSize + zone_size_bytes - 1) /
           zone_size_bytes * zone_size_bytes -
       params_.start_sector * kDefaultSectorSize;
@@ -282,10 +307,10 @@
       CpuToLe(LeToCpu(super_block_.segment0_blkaddr) +
               (LeToCpu(super_block_.segment_count_ckpt) * (1 << log_blks_per_seg)));
 
-  blocks_for_sit =
+  uint32_t blocks_for_sit =
       (LeToCpu(super_block_.segment_count) + kSitEntryPerBlock - 1) / kSitEntryPerBlock;
 
-  sit_segments = (blocks_for_sit + params_.blks_per_seg - 1) / params_.blks_per_seg;
+  uint32_t sit_segments = (blocks_for_sit + params_.blks_per_seg - 1) / params_.blks_per_seg;
 
   super_block_.segment_count_sit = CpuToLe(sit_segments * 2);
 
@@ -293,12 +318,13 @@
       CpuToLe(LeToCpu(super_block_.sit_blkaddr) +
               (LeToCpu(super_block_.segment_count_sit) * params_.blks_per_seg));
 
-  total_valid_blks_available =
+  uint32_t total_valid_blks_available =
       (LeToCpu(super_block_.segment_count) -
        (LeToCpu(super_block_.segment_count_ckpt) + LeToCpu(super_block_.segment_count_sit))) *
       params_.blks_per_seg;
 
-  blocks_for_nat = (total_valid_blks_available + kNatEntryPerBlock - 1) / kNatEntryPerBlock;
+  uint32_t blocks_for_nat =
+      (total_valid_blks_available + kNatEntryPerBlock - 1) / kNatEntryPerBlock;
 
   super_block_.segment_count_nat =
       CpuToLe((blocks_for_nat + params_.blks_per_seg - 1) / params_.blks_per_seg);
@@ -307,9 +333,10 @@
    * This number resizes NAT bitmap area in a CP page.
    * So the threshold is determined not to overflow one CP page
    */
-  sit_bitmap_size = ((LeToCpu(super_block_.segment_count_sit) / 2) << log_blks_per_seg) / 8;
-  max_nat_bitmap_size = 4096 - sizeof(Checkpoint) + 1 - sit_bitmap_size;
-  max_nat_segments = (max_nat_bitmap_size * 8) >> log_blks_per_seg;
+  uint32_t sit_bitmap_size =
+      ((LeToCpu(super_block_.segment_count_sit) / 2) << log_blks_per_seg) / 8;
+  uint32_t max_nat_bitmap_size = 4096 - sizeof(Checkpoint) + 1 - sit_bitmap_size;
+  uint32_t max_nat_segments = (max_nat_bitmap_size * 8) >> log_blks_per_seg;
 
   if (LeToCpu(super_block_.segment_count_nat) > max_nat_segments)
     super_block_.segment_count_nat = CpuToLe(max_nat_segments);
@@ -326,16 +353,17 @@
         LeToCpu(super_block_.segment_count_nat))) *
       params_.blks_per_seg;
 
-  blocks_for_ssa = total_valid_blks_available / params_.blks_per_seg + 1;
+  uint32_t blocks_for_ssa = total_valid_blks_available / params_.blks_per_seg + 1;
 
   super_block_.segment_count_ssa =
       CpuToLe((blocks_for_ssa + params_.blks_per_seg - 1) / params_.blks_per_seg);
 
-  total_meta_segments =
+  uint64_t total_meta_segments =
       LeToCpu(super_block_.segment_count_ckpt) + LeToCpu(super_block_.segment_count_sit) +
       LeToCpu(super_block_.segment_count_nat) + LeToCpu(super_block_.segment_count_ssa);
-  diff = total_meta_segments % (params_.segs_per_sec * params_.secs_per_zone);
-  if (diff)
+
+  if (uint64_t diff = total_meta_segments % (params_.segs_per_sec * params_.secs_per_zone);
+      diff != 0)
     super_block_.segment_count_ssa = CpuToLe(LeToCpu(super_block_.segment_count_ssa) +
                                              (params_.segs_per_sec * params_.secs_per_zone - diff));
 
@@ -374,8 +402,8 @@
   super_block_.meta_ino = CpuToLe(uint32_t{2});
   super_block_.root_ino = CpuToLe(uint32_t{3});
 
-  total_zones = ((LeToCpu(super_block_.segment_count_main) - 1) / params_.segs_per_sec) /
-                params_.secs_per_zone;
+  uint32_t total_zones = ((LeToCpu(super_block_.segment_count_main) - 1) / params_.segs_per_sec) /
+                         params_.secs_per_zone;
   if (total_zones <= 6) {
     printf("\n\tError: %d zones: Need more zones	by shrinking zone size\n", total_zones);
     return ZX_ERR_NO_SPACE;
@@ -419,31 +447,23 @@
 
   ConfigureExtensionList();
 
-  return 0;
+  return ZX_OK;
 }
 
-zx_status_t F2fsMkfs::InitSitArea() {
-  uint32_t blk_size_bytes;
-  uint32_t seg_size_bytes;
-  uint32_t index = 0;
-  uint64_t sit_seg_blk_offset = 0;
-  uint8_t *zero_buf = nullptr;
-  zx_status_t ret;
+zx_status_t MkfsWorker::InitSitArea() {
+  uint32_t blk_size_bytes = 1 << LeToCpu(super_block_.log_blocksize);
+  uint32_t seg_size_bytes = (1 << LeToCpu(super_block_.log_blocks_per_seg)) * blk_size_bytes;
 
-  blk_size_bytes = 1 << LeToCpu(super_block_.log_blocksize);
-  seg_size_bytes = (1 << LeToCpu(super_block_.log_blocks_per_seg)) * blk_size_bytes;
-
-  zero_buf = static_cast<uint8_t *>(calloc(sizeof(uint8_t), seg_size_bytes));
+  uint8_t *zero_buf = static_cast<uint8_t *>(calloc(sizeof(uint8_t), seg_size_bytes));
   if (zero_buf == nullptr) {
     printf("\n\tError: Calloc Failed for sit_zero_buf!!!\n");
     return ZX_ERR_NO_MEMORY;
   }
 
-  sit_seg_blk_offset = LeToCpu(super_block_.sit_blkaddr) * blk_size_bytes;
+  uint64_t sit_seg_blk_offset = LeToCpu(super_block_.sit_blkaddr) * blk_size_bytes;
 
-  for (index = 0; index < (LeToCpu(super_block_.segment_count_sit) / 2); index++) {
-    ret = WriteToDisk(zero_buf, sit_seg_blk_offset, seg_size_bytes);
-    if (ret < 0) {
+  for (uint32_t index = 0; index < (LeToCpu(super_block_.segment_count_sit) / 2); index++) {
+    if (zx_status_t ret = WriteToDisk(zero_buf, sit_seg_blk_offset, seg_size_bytes); ret != ZX_OK) {
       printf("\n\tError: While zeroing out the sit area on disk!!!\n");
       return ret;
     }
@@ -454,28 +474,20 @@
   return ZX_OK;
 }
 
-zx_status_t F2fsMkfs::InitNatArea() {
-  uint32_t blk_size_bytes;
-  uint32_t seg_size_bytes;
-  uint32_t index = 0;
-  uint64_t nat_seg_blk_offset = 0;
-  uint8_t *nat_buf = nullptr;
-  zx_status_t ret;
+zx_status_t MkfsWorker::InitNatArea() {
+  uint32_t blk_size_bytes = 1 << LeToCpu(super_block_.log_blocksize);
+  uint32_t seg_size_bytes = (1 << LeToCpu(super_block_.log_blocks_per_seg)) * blk_size_bytes;
 
-  blk_size_bytes = 1 << LeToCpu(super_block_.log_blocksize);
-  seg_size_bytes = (1 << LeToCpu(super_block_.log_blocks_per_seg)) * blk_size_bytes;
-
-  nat_buf = static_cast<uint8_t *>(calloc(sizeof(uint8_t), seg_size_bytes));
+  uint8_t *nat_buf = static_cast<uint8_t *>(calloc(sizeof(uint8_t), seg_size_bytes));
   if (nat_buf == nullptr) {
     printf("\n\tError: Calloc Failed for nat_zero_blk!!!\n");
     return ZX_ERR_NO_MEMORY;
   }
 
-  nat_seg_blk_offset = LeToCpu(super_block_.nat_blkaddr) * blk_size_bytes;
+  uint64_t nat_seg_blk_offset = LeToCpu(super_block_.nat_blkaddr) * blk_size_bytes;
 
-  for (index = 0; index < (LeToCpu(super_block_.segment_count_nat) / 2); index++) {
-    ret = WriteToDisk(nat_buf, nat_seg_blk_offset, seg_size_bytes);
-    if (ret < 0) {
+  for (uint32_t index = 0; index < (LeToCpu(super_block_.segment_count_nat) / 2); index++) {
+    if (zx_status_t ret = WriteToDisk(nat_buf, nat_seg_blk_offset, seg_size_bytes); ret != ZX_OK) {
       printf("\n\tError: While zeroing out the nat area on disk!!!\n");
       return ret;
     }
@@ -486,22 +498,14 @@
   return ZX_OK;
 }
 
-zx_status_t F2fsMkfs::WriteCheckPointPack() {
-  Checkpoint *ckp = nullptr;
-  SummaryBlock *sum = nullptr;
-  uint32_t blk_size_bytes;
-  uint64_t cp_seg_blk_offset = 0;
-  uint32_t crc = 0;
-  zx_status_t ret;
-  int i;
-
-  ckp = static_cast<Checkpoint *>(calloc(kBlkSize, 1));
+zx_status_t MkfsWorker::WriteCheckPointPack() {
+  Checkpoint *ckp = static_cast<Checkpoint *>(calloc(kBlkSize, 1));
   if (ckp == nullptr) {
     printf("\n\tError: Calloc Failed for Checkpoint!!!\n");
     return ZX_ERR_NO_MEMORY;
   }
 
-  sum = static_cast<SummaryBlock *>(calloc(kBlkSize, 1));
+  SummaryBlock *sum = static_cast<SummaryBlock *>(calloc(kBlkSize, 1));
   if (sum == nullptr) {
     printf("\n\tError: Calloc Failed for summay_node!!!\n");
     return ZX_ERR_NO_MEMORY;
@@ -515,7 +519,7 @@
   ckp->cur_data_segno[0] = CpuToLe(params_.cur_seg[static_cast<int>(CursegType::kCursegHotData)]);
   ckp->cur_data_segno[1] = CpuToLe(params_.cur_seg[static_cast<int>(CursegType::kCursegWarmData)]);
   ckp->cur_data_segno[2] = CpuToLe(params_.cur_seg[static_cast<int>(CursegType::kCursegColdData)]);
-  for (i = 3; i < kMaxActiveNodeLogs; i++) {
+  for (int i = 3; i < kMaxActiveNodeLogs; i++) {
     ckp->cur_node_segno[i] = 0xffffffff;
     ckp->cur_data_segno[i] = 0xffffffff;
   }
@@ -554,15 +558,14 @@
 
   ckp->checksum_offset = CpuToLe(uint32_t{kChecksumOffset});
 
-  crc = F2fsCalCrc32(kF2fsSuperMagic, ckp, LeToCpu(ckp->checksum_offset));
+  uint32_t crc = F2fsCalCrc32(kF2fsSuperMagic, ckp, LeToCpu(ckp->checksum_offset));
   *(reinterpret_cast<uint32_t *>(reinterpret_cast<uint8_t *>(ckp) +
                                  LeToCpu(ckp->checksum_offset))) = crc;
 
-  blk_size_bytes = 1 << LeToCpu(super_block_.log_blocksize);
-  cp_seg_blk_offset = LeToCpu(super_block_.segment0_blkaddr) * blk_size_bytes;
+  uint32_t blk_size_bytes = 1 << LeToCpu(super_block_.log_blocksize);
+  uint64_t cp_seg_blk_offset = LeToCpu(super_block_.segment0_blkaddr) * blk_size_bytes;
 
-  ret = WriteToDisk(ckp, cp_seg_blk_offset, kBlkSize);
-  if (ret < 0) {
+  if (zx_status_t ret = WriteToDisk(ckp, cp_seg_blk_offset, kBlkSize); ret != ZX_OK) {
     printf("\n\tError: While writing the ckp to disk!!!\n");
     return ret;
   }
@@ -575,8 +578,7 @@
   sum->entries[0].ofs_in_node = 0;
 
   cp_seg_blk_offset += blk_size_bytes;
-  ret = WriteToDisk(sum, cp_seg_blk_offset, kBlkSize);
-  if (ret < 0) {
+  if (zx_status_t ret = WriteToDisk(sum, cp_seg_blk_offset, kBlkSize); ret != ZX_OK) {
     printf("\n\tError: While writing the sum_blk to disk!!!\n");
     return ret;
   }
@@ -586,8 +588,7 @@
   SetSumType((&sum->footer), kSumTypeData);
 
   cp_seg_blk_offset += blk_size_bytes;
-  ret = WriteToDisk(sum, cp_seg_blk_offset, kBlkSize);
-  if (ret < 0) {
+  if (zx_status_t ret = WriteToDisk(sum, cp_seg_blk_offset, kBlkSize); ret != ZX_OK) {
     printf("\n\tError: While writing the sum_blk to disk!!!\n");
     return ret;
   }
@@ -601,7 +602,7 @@
   sum->sit_j.entries[0].segno = ckp->cur_node_segno[0];
   sum->sit_j.entries[0].se.vblocks =
       CpuToLe(uint16_t{(static_cast<int>(CursegType::kCursegHotNode) << 10) | 1});
-  F2fsSetBit(0, sum->sit_j.entries[0].se.valid_map);
+  f2fs_set_bit(0, reinterpret_cast<char *>(sum->sit_j.entries[0].se.valid_map));
   sum->sit_j.entries[1].segno = ckp->cur_node_segno[1];
   sum->sit_j.entries[1].se.vblocks =
       CpuToLe(uint16_t{(static_cast<int>(CursegType::kCursegWarmNode) << 10)});
@@ -613,7 +614,7 @@
   sum->sit_j.entries[3].segno = ckp->cur_data_segno[0];
   sum->sit_j.entries[3].se.vblocks =
       CpuToLe(uint16_t{(static_cast<uint16_t>(CursegType::kCursegHotData) << 10) | 1});
-  F2fsSetBit(0, sum->sit_j.entries[3].se.valid_map);
+  f2fs_set_bit(0, reinterpret_cast<char *>(sum->sit_j.entries[3].se.valid_map));
   sum->sit_j.entries[4].segno = ckp->cur_data_segno[1];
   sum->sit_j.entries[4].se.vblocks =
       CpuToLe(uint16_t{(static_cast<int>(CursegType::kCursegWarmData) << 10)});
@@ -622,8 +623,7 @@
       CpuToLe(uint16_t{(static_cast<int>(CursegType::kCursegColdData) << 10)});
 
   cp_seg_blk_offset += blk_size_bytes;
-  ret = WriteToDisk(sum, cp_seg_blk_offset, kBlkSize);
-  if (ret < 0) {
+  if (zx_status_t ret = WriteToDisk(sum, cp_seg_blk_offset, kBlkSize); ret != ZX_OK) {
     printf("\n\tError: While writing the sum_blk to disk!!!\n");
     return ret;
   }
@@ -636,8 +636,7 @@
   sum->entries[0].ofs_in_node = 0;
 
   cp_seg_blk_offset += blk_size_bytes;
-  ret = WriteToDisk(sum, cp_seg_blk_offset, kBlkSize);
-  if (ret < 0) {
+  if (zx_status_t ret = WriteToDisk(sum, cp_seg_blk_offset, kBlkSize); ret != ZX_OK) {
     printf("\n\tError: While writing the sum_blk to disk!!!\n");
     return ret;
   }
@@ -647,8 +646,7 @@
   SetSumType((&sum->footer), kSumTypeNode);
 
   cp_seg_blk_offset += blk_size_bytes;
-  ret = WriteToDisk(sum, cp_seg_blk_offset, kBlkSize);
-  if (ret < 0) {
+  if (zx_status_t ret = WriteToDisk(sum, cp_seg_blk_offset, kBlkSize); ret != ZX_OK) {
     printf("\n\tError: While writing the sum_blk to disk!!!\n");
     return ret;
   }
@@ -657,16 +655,14 @@
   memset(sum, 0, sizeof(SummaryBlock));
   SetSumType((&sum->footer), kSumTypeNode);
   cp_seg_blk_offset += blk_size_bytes;
-  ret = WriteToDisk(sum, cp_seg_blk_offset, kBlkSize);
-  if (ret < 0) {
+  if (zx_status_t ret = WriteToDisk(sum, cp_seg_blk_offset, kBlkSize); ret != ZX_OK) {
     printf("\n\tError: While writing the sum_blk to disk!!!\n");
     return ret;
   }
 
   /* 8. cp page2 */
   cp_seg_blk_offset += blk_size_bytes;
-  ret = WriteToDisk(ckp, cp_seg_blk_offset, kBlkSize);
-  if (ret < 0) {
+  if (zx_status_t ret = WriteToDisk(ckp, cp_seg_blk_offset, kBlkSize); ret != ZX_OK) {
     printf("\n\tError: While writing the ckp to disk!!!\n");
     return ret;
   }
@@ -682,8 +678,7 @@
 
   cp_seg_blk_offset =
       (LeToCpu(super_block_.segment0_blkaddr) + params_.blks_per_seg) * blk_size_bytes;
-  ret = WriteToDisk(ckp, cp_seg_blk_offset, kBlkSize);
-  if (ret < 0) {
+  if (zx_status_t ret = WriteToDisk(ckp, cp_seg_blk_offset, kBlkSize); ret != ZX_OK) {
     printf("\n\tError: While writing the ckp to disk!!!\n");
     return ret;
   }
@@ -693,18 +688,13 @@
   return ZX_OK;
 }
 
-zx_status_t F2fsMkfs::WriteSuperBlock() {
-  uint32_t index = 0;
-  uint8_t *zero_buff;
-  zx_status_t ret;
-
-  zero_buff = static_cast<uint8_t *>(calloc(kBlkSize, 1));
+zx_status_t MkfsWorker::WriteSuperBlock() {
+  uint8_t *zero_buff = static_cast<uint8_t *>(calloc(kBlkSize, 1));
 
   memcpy(zero_buff + kSuperOffset, &super_block_, sizeof(super_block_));
 
-  for (index = 0; index < 2; index++) {
-    ret = WriteToDisk(zero_buff, index * kBlkSize, kBlkSize);
-    if (ret < 0) {
+  for (uint32_t index = 0; index < 2; index++) {
+    if (zx_status_t ret = WriteToDisk(zero_buff, index * kBlkSize, kBlkSize); ret != ZX_OK) {
       printf("\n\tError: While while writing supe_blk	on disk!!! index : %d\n", index);
       return ret;
     }
@@ -714,14 +704,8 @@
   return ZX_OK;
 }
 
-zx_status_t F2fsMkfs::WriteRootInode() {
-  Node *raw_node = nullptr;
-  uint32_t blk_size_bytes;
-  uint64_t data_blk_nor;
-  uint64_t main_area_node_seg_blk_offset = 0;
-  zx_status_t ret;
-
-  raw_node = static_cast<Node *>(calloc(kBlkSize, 1));
+zx_status_t MkfsWorker::WriteRootInode() {
+  Node *raw_node = static_cast<Node *>(calloc(kBlkSize, 1));
   if (raw_node == nullptr) {
     printf("\n\tError: Calloc Failed for raw_node!!!\n");
     return ZX_ERR_NO_MEMORY;
@@ -739,7 +723,7 @@
   raw_node->i.i_uid = CpuToLe(getuid());
   raw_node->i.i_gid = CpuToLe(getgid());
 
-  blk_size_bytes = 1 << LeToCpu(super_block_.log_blocksize);
+  uint32_t blk_size_bytes = 1 << LeToCpu(super_block_.log_blocksize);
   raw_node->i.i_size = CpuToLe(1 * blk_size_bytes); /* dentry */
   raw_node->i.i_blocks = CpuToLe(uint64_t{2});
 
@@ -756,7 +740,7 @@
   raw_node->i.i_flags = 0;
   raw_node->i.i_current_depth = CpuToLe(uint32_t{1});
 
-  data_blk_nor =
+  uint64_t data_blk_nor =
       LeToCpu(super_block_.main_blkaddr) +
       params_.cur_seg[static_cast<int>(CursegType::kCursegHotData)] * params_.blks_per_seg;
   raw_node->i.i_addr[0] = CpuToLe(data_blk_nor);
@@ -765,21 +749,21 @@
   raw_node->i.i_ext.blk_addr = CpuToLe(data_blk_nor);
   raw_node->i.i_ext.len = CpuToLe(uint32_t{1});
 
-  main_area_node_seg_blk_offset = LeToCpu(super_block_.main_blkaddr);
+  uint64_t main_area_node_seg_blk_offset = LeToCpu(super_block_.main_blkaddr);
   main_area_node_seg_blk_offset +=
       params_.cur_seg[static_cast<int>(CursegType::kCursegHotNode)] * params_.blks_per_seg;
   main_area_node_seg_blk_offset *= blk_size_bytes;
 
-  ret = WriteToDisk(raw_node, main_area_node_seg_blk_offset, kBlkSize);
-  if (ret < 0) {
+  if (zx_status_t ret = WriteToDisk(raw_node, main_area_node_seg_blk_offset, kBlkSize);
+      ret != ZX_OK) {
     printf("\n\tError: While writing the raw_node to disk!!!, size = %lu\n", sizeof(Node));
     return ret;
   }
 
   memset(raw_node, 0xff, sizeof(Node));
 
-  ret = WriteToDisk(raw_node, main_area_node_seg_blk_offset + 4096, kBlkSize);
-  if (ret < 0) {
+  if (zx_status_t ret = WriteToDisk(raw_node, main_area_node_seg_blk_offset + 4096, kBlkSize);
+      ret != ZX_OK) {
     printf("\n\tError: While writing the raw_node to disk!!!\n");
     return ret;
   }
@@ -787,13 +771,8 @@
   return ZX_OK;
 }
 
-zx_status_t F2fsMkfs::UpdateNatRoot() {
-  NatBlock *nat_blk = nullptr;
-  uint32_t blk_size_bytes;
-  uint64_t nat_seg_blk_offset = 0;
-  zx_status_t ret;
-
-  nat_blk = static_cast<NatBlock *>(calloc(kBlkSize, 1));
+zx_status_t MkfsWorker::UpdateNatRoot() {
+  NatBlock *nat_blk = static_cast<NatBlock *>(calloc(kBlkSize, 1));
   if (nat_blk == nullptr) {
     printf("\n\tError: Calloc Failed for nat_blk!!!\n");
     return ZX_ERR_NO_MEMORY;
@@ -813,12 +792,11 @@
   nat_blk->entries[super_block_.meta_ino].block_addr = CpuToLe(uint32_t{1});
   nat_blk->entries[super_block_.meta_ino].ino = super_block_.meta_ino;
 
-  blk_size_bytes = 1 << LeToCpu(super_block_.log_blocksize);
+  uint32_t blk_size_bytes = 1 << LeToCpu(super_block_.log_blocksize);
 
-  nat_seg_blk_offset = LeToCpu(super_block_.nat_blkaddr) * blk_size_bytes;
+  uint64_t nat_seg_blk_offset = LeToCpu(super_block_.nat_blkaddr) * blk_size_bytes;
 
-  ret = WriteToDisk(nat_blk, nat_seg_blk_offset, kBlkSize);
-  if (ret < 0) {
+  if (zx_status_t ret = WriteToDisk(nat_blk, nat_seg_blk_offset, kBlkSize); ret != ZX_OK) {
     printf("\n\tError: While writing the nat_blk set0 to disk!!!\n");
     return ret;
   }
@@ -827,13 +805,8 @@
   return ZX_OK;
 }
 
-zx_status_t F2fsMkfs::AddDefaultDentryRoot() {
-  DentryBlock *dent_blk = nullptr;
-  uint32_t blk_size_bytes;
-  uint64_t data_blk_offset = 0;
-  zx_status_t ret;
-
-  dent_blk = static_cast<DentryBlock *>(calloc(kBlkSize, 1));
+zx_status_t MkfsWorker::AddDefaultDentryRoot() {
+  DentryBlock *dent_blk = static_cast<DentryBlock *>(calloc(kBlkSize, 1));
   if (dent_blk == nullptr) {
     printf("\n\tError: Calloc Failed for dent_blk!!!\n");
     return ZX_ERR_NO_MEMORY;
@@ -853,14 +826,13 @@
 
   /* bitmap for . and .. */
   dent_blk->dentry_bitmap[0] = (1 << 1) | (1 << 0);
-  blk_size_bytes = 1 << LeToCpu(super_block_.log_blocksize);
-  data_blk_offset =
+  uint32_t blk_size_bytes = 1 << LeToCpu(super_block_.log_blocksize);
+  uint64_t data_blk_offset =
       (LeToCpu(super_block_.main_blkaddr) +
        params_.cur_seg[static_cast<int>(CursegType::kCursegHotData)] * params_.blks_per_seg) *
       blk_size_bytes;
 
-  ret = WriteToDisk(dent_blk, data_blk_offset, kBlkSize);
-  if (ret < 0) {
+  if (zx_status_t ret = WriteToDisk(dent_blk, data_blk_offset, kBlkSize); ret != ZX_OK) {
     printf("\n\tError: While writing the dentry_blk to disk!!!\n");
     return ret;
   }
@@ -869,35 +841,26 @@
   return ZX_OK;
 }
 
-zx_status_t F2fsMkfs::CreateRootDir() {
-  int8_t err = 0;
-
-  err = WriteRootInode();
-  if (err < 0) {
-    printf("\n\tError: Failed to write root inode!!!\n");
-    goto exit;
+zx_status_t MkfsWorker::CreateRootDir() {
+  zx_status_t err = ZX_OK;
+  const char err_msg[] = "Error creating root directry: ";
+  if (err = WriteRootInode(); err != ZX_OK) {
+    FX_LOGS(ERROR) << err_msg << "Failed to write root inode" << err;
+    return err;
   }
-
-  err = UpdateNatRoot();
-  if (err < 0) {
-    printf("\n\tError: Failed to update NAT for root!!!\n");
-    goto exit;
+  if (err = UpdateNatRoot(); err != ZX_OK) {
+    FX_LOGS(ERROR) << err_msg << "Failed to update NAT for root" << err;
+    return err;
   }
-
-  err = AddDefaultDentryRoot();
-  if (err < 0) {
-    printf("\n\tError: Failed to add default dentries for root!!!\n");
-    goto exit;
+  if (err = AddDefaultDentryRoot(); err != ZX_OK) {
+    FX_LOGS(ERROR) << err_msg << "Failed to add default dentries for root" << err;
+    return err;
   }
-exit:
-  if (err)
-    printf("\n\tError: Could not create the root directory!!!\n");
-
   return err;
 }
 
 #if 0  // porting needed
-// int F2fsMkfs::F2fsTrimDevice()
+// int MkfsWorker::F2fsTrimDevice()
 // {
 //         uint64_t range[2];
 //         stat stat_buf;
@@ -921,53 +884,45 @@
 // }
 #endif
 
-zx_status_t F2fsMkfs::FormatDevice() {
-  zx_status_t err = 0;
-
-  err = PrepareSuperBlock();
-  if (err < 0)
-    goto exit;
-
+zx_status_t MkfsWorker::FormatDevice() {
+  zx_status_t err = ZX_OK;
+  const char err_msg[] = "Error formatting the device: ";
+  if (err = PrepareSuperBlock(); err != ZX_OK) {
+    FX_LOGS(ERROR) << err_msg << "Failed to prepare superblock information";
+    return err;
+  }
 #if 0  // porting needed
-  // TRIM is not supported
-  // err = f2fs_trim_device();
-  // if (err < 0) {
-  //   printf("\n\tError: Failed to trim whole device!!!\n");
-  //   goto exit;
-  // }
+    // TRIM is not supported
+    // err = f2fs_trim_device();
+    // if (err < 0) {
+    //   printf("\n\tError: Failed to trim whole device!!!\n");
+    //   break;
+    // }
 #endif
-  err = InitSitArea();
-  if (err < 0) {
-    printf("\n\tError: Failed to Initialise the SIT AREA!!!\n");
-    goto exit;
+  if (err = InitSitArea(); err != ZX_OK) {
+    FX_LOGS(ERROR) << err_msg << "Failed to Initialise the SIT AREA" << err;
+    return err;
   }
 
-  err = InitNatArea();
-  if (err < 0) {
-    printf("\n\tError: Failed to Initialise the NAT AREA!!!\n");
-    goto exit;
+  if (err = InitNatArea(); err != ZX_OK) {
+    FX_LOGS(ERROR) << err_msg << "Failed to Initialise the NAT AREA" << err;
+    return err;
   }
 
-  err = CreateRootDir();
-  if (err < 0) {
-    printf("\n\tError: Failed to create the root directory!!!\n");
-    goto exit;
+  if (err = CreateRootDir(); err != ZX_OK) {
+    FX_LOGS(ERROR) << err_msg << "Failed to create the root directory" << err;
+    return err;
   }
 
-  err = WriteCheckPointPack();
-  if (err < 0) {
-    printf("\n\tError: Failed to write the check point pack!!!\n");
-    goto exit;
+  if (err = WriteCheckPointPack(); err != ZX_OK) {
+    FX_LOGS(ERROR) << err_msg << "Failed to write the check point pack" << err;
+    return err;
   }
 
-  err = WriteSuperBlock();
-  if (err < 0) {
-    printf("\n\tError: Failed to write the Super Block!!!\n");
-    goto exit;
+  if (err = WriteSuperBlock(); err != ZX_OK) {
+    FX_LOGS(ERROR) << err_msg << "Failed to write the Super Block" << err;
+    return err;
   }
-exit:
-  if (err)
-    printf("\n\tError: Could not format the device!!!\n");
 
   /*
    * We should call fsync() to flush out all the dirty pages
@@ -978,4 +933,14 @@
   return err;
 }
 
+zx_status_t Mkfs(Bcache *bc, int argc, char **argv) {
+  MkfsWorker mkfs(bc);
+
+  if (zx_status_t ret = mkfs.ParseOptions(argc, argv); ret != ZX_OK) {
+    return ret;
+  }
+
+  return mkfs.DoMkfs();
+}
+
 }  // namespace f2fs
diff --git a/mkfs.h b/mkfs.h
index 22f69eb..f5bc25c 100644
--- a/mkfs.h
+++ b/mkfs.h
@@ -8,38 +8,32 @@
 namespace f2fs {
 
 constexpr uint32_t kChecksumOffset = 4092;
-struct MkfsOptions {
-  // TODO: Add mkfs options
-  /*
-   Usage: mkfs.f2fs [options] device [sectors]
-  [options]:
-          -l label
-          -a heap-based allocation [default:0]
-          -o overprovision ratio [default:5]
-          -s # of segments per section [default:1]
-          -z # of sections per zone [default:1]
-          -e [extention list] e.g. "mp3,gif,mov"
-   */
 
+static const char* kMediaExtList[] = {"jpg", "gif", "png",  "avi", "divx", "mp4", "mp3", "3gp",
+                                      "wmv", "wma", "mpeg", "mkv", "mov",  "asx", "asf", "wmx",
+                                      "svi", "wvx", "wm",   "mpg", "mpe",  "rm",  "ogg"};
+
+struct MkfsOptions {
   char* label = nullptr;
-  bool heap_based_allocation = false;
+  bool heap_based_allocation = true;
   uint32_t overprovision_ratio = 5;
-  uint32_t num_of_seg_per_sec = 1;
-  uint32_t num_of_sec_per_zone = 1;
-  char* extention_list = nullptr;
+  uint32_t segs_per_sec = 1;
+  uint32_t secs_per_zone = 1;
+  char* extension_list = nullptr;
 };
 
-class F2fsMkfs {
+class MkfsWorker {
  public:
-  explicit F2fsMkfs(std::unique_ptr<f2fs::Bcache> bc, const MkfsOptions& mkfs_options);
+  explicit MkfsWorker(Bcache* bc);
 
-  ~F2fsMkfs() = default;
+  ~MkfsWorker() = default;
 
-  zx_status_t Mkfs();
+  zx_status_t DoMkfs();
+  zx_status_t ParseOptions(int argc, char** argv);
 
  private:
-  std::unique_ptr<f2fs::Bcache> bc_;
-  [[maybe_unused]] const MkfsOptions& mkfs_options_;
+  Bcache* bc_;
+  MkfsOptions mkfs_options_;
 
   // F2FS Parameter
   GlobalParameters params_;
@@ -49,10 +43,6 @@
   zx_status_t GetDeviceInfo();
   zx_status_t FormatDevice();
 
-  void AsciiToUnicode(const std::string& in_string, std::u16string* out_string);
-
-  inline int F2fsSetBit(uint32_t nr, uint8_t* addr);
-  int8_t LogBase2(uint32_t num);
   void ConfigureExtensionList();
 
   zx_status_t WriteToDisk(void* buf, uint64_t offset, size_t length);
@@ -68,12 +58,19 @@
   zx_status_t AddDefaultDentryRoot();
   zx_status_t CreateRootDir();
 
+  void PrintUsage();
+  void PrintCurrentOption();
+
 #if 0  // porting needed
   // TODO: Trim support
   // int f2fs_trim_device()
 #endif
 };
 
+zx_status_t Mkfs(Bcache* bc, int argc, char** argv);
+
+void AsciiToUnicode(const std::string& in_string, std::u16string* out_string);
+
 }  // namespace f2fs
 
 #endif  // THIRD_PARTY_F2FS_MKFS_H_
diff --git a/patches/0007-f2fs-Add-filesystem-specific-mkfs-option.patch b/patches/0007-f2fs-Add-filesystem-specific-mkfs-option.patch
new file mode 100644
index 0000000..6fa9128
--- /dev/null
+++ b/patches/0007-f2fs-Add-filesystem-specific-mkfs-option.patch
@@ -0,0 +1,150 @@
+From 9fca37737ec96fd715ab24ab8199dd5c9a9d53df Mon Sep 17 00:00:00 2001
+From: Dongjin Kim <dongjin_.kim@samsung.com>
+Date: Tue, 8 Jun 2021 19:23:09 +0900
+Subject: [PATCH] [f2fs] Add filesystem specific mkfs option
+
+"-p" option is delivered to filesystem, and filesystem will parse it.
+(currently only for f2fs)
+
+Change-Id: Id10a624c5e1177ca53b3f900fadf5a0646f212c2
+---
+ .../cpp/include/fs-management/admin.h         |  2 +
+ src/lib/storage/fs_management/cpp/mkfs.cc     | 53 ++++++++++++++++++-
+ src/storage/bin/mkfs/main.cc                  | 10 +++-
+ 3 files changed, 62 insertions(+), 3 deletions(-)
+
+diff --git a/src/lib/storage/fs_management/cpp/include/fs-management/admin.h b/src/lib/storage/fs_management/cpp/include/fs-management/admin.h
+index 6d5b7476e9c..420d0ea091e 100644
+--- a/src/lib/storage/fs_management/cpp/include/fs-management/admin.h
++++ b/src/lib/storage/fs_management/cpp/include/fs-management/admin.h
+@@ -58,6 +58,8 @@ typedef struct mkfs_options {
+   // The initial number of inodes to allocate space for. If 0, a default is used. Only supported
+   // for blobfs.
+   uint64_t num_inodes;
++  // Filesystem-specific options (currently for f2fs)
++  char* private_options = nullptr;
+ } mkfs_options_t;
+ 
+ __EXPORT
+diff --git a/src/lib/storage/fs_management/cpp/mkfs.cc b/src/lib/storage/fs_management/cpp/mkfs.cc
+index 506512aaf51..cc6030084a4 100644
+--- a/src/lib/storage/fs_management/cpp/mkfs.cc
++++ b/src/lib/storage/fs_management/cpp/mkfs.cc
+@@ -18,6 +18,7 @@
+ 
+ #include <iterator>
+ #include <new>
++#include <sstream>
+ #include <vector>
+ 
+ #include <fbl/algorithm.h>
+@@ -97,6 +98,55 @@ zx_status_t MkfsFat(const char* device_path, LaunchCallback cb, const mkfs_optio
+   return cb(argv.size() - 1, argv.data(), NULL, NULL, 0);
+ }
+ 
++zx_status_t MkfsF2fs(const char* device_path, LaunchCallback cb, const mkfs_options_t* options) {
++  fbl::unique_fd device_fd;
++  device_fd.reset(open(device_path, O_RDWR));
++  if (!device_fd) {
++    fprintf(stderr, "Failed to open device\n");
++    return ZX_ERR_BAD_STATE;
++  }
++  zx::channel block_device;
++  zx_status_t status =
++      fdio_get_service_handle(device_fd.release(), block_device.reset_and_get_address());
++  if (status != ZX_OK) {
++    return status;
++  }
++
++  const std::string tool_path = fs_management::GetBinaryPath("f2fs");
++  std::vector<const char*> argv = {tool_path.c_str()};
++  argv.push_back("mkfs");
++
++  std::vector<std::string> private_options_buf(10, "");
++
++  if (options->private_options != nullptr) {
++    std::istringstream iss(options->private_options);
++    std::string token;
++    uint32_t idx = 0;
++
++    while (std::getline(iss, token, ' ')) {
++      if (token == "")
++        continue;
++
++      if (idx >= private_options_buf.size()) {
++        private_options_buf.resize(private_options_buf.size() * 2, "");
++      }
++      private_options_buf[idx++] = token;
++    }
++
++    for (uint32_t i = 0; i < idx; i++) {
++      argv.push_back(private_options_buf[i].c_str());
++    }
++  }
++
++  argv.push_back(nullptr);
++
++  zx_handle_t hnd = block_device.release();
++  uint32_t id = FS_HANDLE_BLOCK_DEVICE_ID;
++  status =
++      static_cast<zx_status_t>(cb(static_cast<int>(argv.size() - 1), argv.data(), &hnd, &id, 1));
++  return status;
++}
++
+ }  // namespace
+ 
+ __EXPORT
+@@ -118,8 +168,7 @@ zx_status_t mkfs(const char* device_path, disk_format_t df, LaunchCallback cb,
+       return MkfsNativeFs(fs_management::GetBinaryPath("blobfs").c_str(), device_path, cb, options,
+                           true);
+     case DISK_FORMAT_F2FS:
+-      return MkfsNativeFs(fs_management::GetBinaryPath("f2fs").c_str(), device_path, cb, options,
+-                          true);
++      return MkfsF2fs(device_path, cb, options);
+     default:
+       return ZX_ERR_NOT_SUPPORTED;
+   }
+diff --git a/src/storage/bin/mkfs/main.cc b/src/storage/bin/mkfs/main.cc
+index 9d370619483..0124d28f1e8 100644
+--- a/src/storage/bin/mkfs/main.cc
++++ b/src/storage/bin/mkfs/main.cc
+@@ -40,6 +40,10 @@ int usage(void) {
+           " -s|--fvm_data_slices SLICES   If block device is on top of a FVM,\n"
+           "                               the filesystem will have at least SLICES slices\n"
+           "                               allocated for data.\n");
++  fprintf(stderr, " -p|--private_options OPTIONS  Filesystem-specific options, wrapped in \"\".\n");
++  fprintf(stderr,
++          "                               It is delivered to filesystem without parsing,\n");
++  fprintf(stderr, "                               and the filesystem will parse it.\n");
+   fprintf(stderr, " values for 'filesystem' include:\n");
+   for (size_t i = 0; i < countof(FILESYSTEMS); i++) {
+     fprintf(stderr, "  '%s'\n", FILESYSTEMS[i].name);
+@@ -53,13 +57,14 @@ int parse_args(int argc, char** argv, mkfs_options_t* options, disk_format_t* df
+       {"help", no_argument, NULL, 'h'},
+       {"verbose", no_argument, NULL, 'v'},
+       {"fvm_data_slices", required_argument, NULL, 's'},
++      {"private_options", required_argument, NULL, 'p'},
+       {0, 0, 0, 0},
+   };
+ 
+   int opt_index = -1;
+   int c = -1;
+ 
+-  while ((c = getopt_long(argc, argv, "hvs:", cmds, &opt_index)) >= 0) {
++  while ((c = getopt_long(argc, argv, "hvs:p:", cmds, &opt_index)) >= 0) {
+     switch (c) {
+       case 'v':
+         options->verbose = true;
+@@ -73,6 +78,9 @@ int parse_args(int argc, char** argv, mkfs_options_t* options, disk_format_t* df
+         break;
+       case 'h':
+         return usage();
++      case 'p':
++        options->private_options = optarg;
++        break;
+       default:
+         break;
+     };
+-- 
+2.25.1
+
diff --git a/test/unit/mkfs.cc b/test/unit/mkfs.cc
index 08669ce..432e7f3 100644
--- a/test/unit/mkfs.cc
+++ b/test/unit/mkfs.cc
@@ -1,3 +1,437 @@
 // Copyright 2021 The Fuchsia Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
+
+#include <string.h>
+
+#include <sstream>
+
+#include <block-client/cpp/fake-device.h>
+#include <gtest/gtest.h>
+
+#include "third_party/f2fs/f2fs.h"
+
+namespace f2fs {
+namespace {
+
+using block_client::FakeBlockDevice;
+
+constexpr uint64_t kBlockCount = 819200;
+constexpr uint32_t kBlockSize = 512;
+
+const MkfsOptions default_option;
+
+enum class ArgType {
+  Label,
+  SegsPerSec,
+  SecsPerZone,
+  Extension,
+  Heap,
+  OP,
+};
+
+void AddArg(std::vector<const char *> &argv, ArgType t, const char *val) {
+  switch (t) {
+    case ArgType::Label:
+      argv.push_back("-l");
+      break;
+    case ArgType::SegsPerSec:
+      argv.push_back("-s");
+      break;
+    case ArgType::SecsPerZone:
+      argv.push_back("-z");
+      break;
+    case ArgType::Extension:
+      argv.push_back("-e");
+      break;
+    case ArgType::Heap:
+      argv.push_back("-a");
+      break;
+    case ArgType::OP:
+      argv.push_back("-o");
+      break;
+  }
+  argv.push_back(val);
+}
+
+void PrintArg(std::vector<const char *> &argv) {
+  std::cout << "mkfs arg: ";
+  for (uint32_t i = 0; i < argv.size(); i++)
+    std::cout << (argv.data())[i] << " ";
+  std::cout << std::endl;
+}
+
+void DoMkfs(Bcache *bc, std::vector<const char *> &argv, bool expect_success) {
+  if (expect_success)
+    ASSERT_EQ(Mkfs(bc, static_cast<int>(argv.size()), const_cast<char **>(argv.data())), ZX_OK);
+  else
+    ASSERT_NE(Mkfs(bc, static_cast<int>(argv.size()), const_cast<char **>(argv.data())), ZX_OK);
+}
+
+void ReadSuperblock(Bcache *bc, SuperBlock *sb) { ASSERT_EQ(LoadSuperblock(bc, sb), ZX_OK); }
+
+void ReadCheckpoint(Bcache *bc, SuperBlock &sb, Checkpoint *ckp) {
+  char buf[4096];
+  ASSERT_EQ(bc->Readblk(sb.segment0_blkaddr, buf), ZX_OK);
+  memcpy(ckp, buf, sizeof(Checkpoint));
+}
+
+void VerifyLabel(SuperBlock &sb, const char *label) {
+  std::string vol_label(label);
+  std::u16string volume_name;
+  AsciiToUnicode(vol_label, &volume_name);
+
+  uint16_t volume_name_arr[512];
+  volume_name.copy(reinterpret_cast<char16_t *>(volume_name_arr), vol_label.size());
+  volume_name_arr[vol_label.size()] = '\0';
+  ASSERT_EQ(memcmp(sb.volume_name, volume_name_arr, vol_label.size() + 1), 0);
+}
+
+void VerifySegsPerSec(SuperBlock &sb, uint32_t segs_per_sec) {
+  ASSERT_EQ(sb.segs_per_sec, segs_per_sec);
+}
+
+void VerifySecsPerZone(SuperBlock &sb, uint32_t secs_per_zone) {
+  ASSERT_EQ(sb.secs_per_zone, secs_per_zone);
+}
+
+void VerifyExtensionList(SuperBlock &sb, const char *extensions) {
+  ASSERT_LE(sb.extension_count, static_cast<uint32_t>(kMaxExtension));
+  uint32_t extension_iter = 0;
+  for (const char *ext_item : kMediaExtList) {
+    ASSERT_LT(extension_iter, sb.extension_count);
+    ASSERT_EQ(strcmp(reinterpret_cast<char *>(sb.extension_list[extension_iter++]), ext_item), 0);
+  }
+
+  ASSERT_EQ(extension_iter, sizeof(kMediaExtList) / sizeof(const char *));
+
+  std::istringstream iss(extensions);
+  std::string token;
+
+  while (std::getline(iss, token, ',')) {
+    ASSERT_LE(extension_iter, sb.extension_count);
+    ASSERT_EQ(strcmp(reinterpret_cast<char *>(sb.extension_list[extension_iter++]), token.c_str()),
+              0);
+    if (extension_iter >= kMaxExtension)
+      break;
+  }
+
+  ASSERT_EQ(extension_iter, sb.extension_count);
+}
+
+void VerifyHeapBasedAllocation(SuperBlock &sb, Checkpoint &ckp, bool is_heap_based) {
+  uint32_t total_zones =
+      ((LeToCpu(sb.segment_count_main) - 1) / sb.segs_per_sec) / sb.secs_per_zone;
+  ASSERT_GT(total_zones, static_cast<uint32_t>(6));
+
+  uint32_t cur_seg[6];
+  if (is_heap_based) {
+    cur_seg[static_cast<int>(CursegType::kCursegHotNode)] =
+        (total_zones - 1) * sb.segs_per_sec * sb.secs_per_zone +
+        ((sb.secs_per_zone - 1) * sb.segs_per_sec);
+    cur_seg[static_cast<int>(CursegType::kCursegWarmNode)] =
+        cur_seg[static_cast<int>(CursegType::kCursegHotNode)] - sb.segs_per_sec * sb.secs_per_zone;
+    cur_seg[static_cast<int>(CursegType::kCursegColdNode)] =
+        cur_seg[static_cast<int>(CursegType::kCursegWarmNode)] - sb.segs_per_sec * sb.secs_per_zone;
+    cur_seg[static_cast<int>(CursegType::kCursegHotData)] =
+        cur_seg[static_cast<int>(CursegType::kCursegColdNode)] - sb.segs_per_sec * sb.secs_per_zone;
+    cur_seg[static_cast<int>(CursegType::kCursegColdData)] = 0;
+    cur_seg[static_cast<int>(CursegType::kCursegWarmData)] =
+        cur_seg[static_cast<int>(CursegType::kCursegColdData)] + sb.segs_per_sec * sb.secs_per_zone;
+  } else {
+    cur_seg[static_cast<int>(CursegType::kCursegHotNode)] = 0;
+    cur_seg[static_cast<int>(CursegType::kCursegWarmNode)] =
+        cur_seg[static_cast<int>(CursegType::kCursegHotNode)] + sb.segs_per_sec * sb.secs_per_zone;
+    cur_seg[static_cast<int>(CursegType::kCursegColdNode)] =
+        cur_seg[static_cast<int>(CursegType::kCursegWarmNode)] + sb.segs_per_sec * sb.secs_per_zone;
+    cur_seg[static_cast<int>(CursegType::kCursegHotData)] =
+        cur_seg[static_cast<int>(CursegType::kCursegColdNode)] + sb.segs_per_sec * sb.secs_per_zone;
+    cur_seg[static_cast<int>(CursegType::kCursegColdData)] =
+        cur_seg[static_cast<int>(CursegType::kCursegHotData)] + sb.segs_per_sec * sb.secs_per_zone;
+    cur_seg[static_cast<int>(CursegType::kCursegWarmData)] =
+        cur_seg[static_cast<int>(CursegType::kCursegColdData)] + sb.segs_per_sec * sb.secs_per_zone;
+  }
+
+  ASSERT_EQ(ckp.cur_node_segno[0], cur_seg[static_cast<int>(CursegType::kCursegHotNode)]);
+  ASSERT_EQ(ckp.cur_node_segno[1], cur_seg[static_cast<int>(CursegType::kCursegWarmNode)]);
+  ASSERT_EQ(ckp.cur_node_segno[2], cur_seg[static_cast<int>(CursegType::kCursegColdNode)]);
+  ASSERT_EQ(ckp.cur_data_segno[0], cur_seg[static_cast<int>(CursegType::kCursegHotData)]);
+  ASSERT_EQ(ckp.cur_data_segno[1], cur_seg[static_cast<int>(CursegType::kCursegWarmData)]);
+  ASSERT_EQ(ckp.cur_data_segno[2], cur_seg[static_cast<int>(CursegType::kCursegColdData)]);
+}
+
+void VerifyOP(SuperBlock &sb, Checkpoint &ckp, uint32_t op_ratio) {
+  uint32_t overprov_segment_count =
+      CpuToLe((LeToCpu(sb.segment_count_main) - LeToCpu(ckp.rsvd_segment_count)) * op_ratio / 100 +
+              LeToCpu(ckp.rsvd_segment_count));
+  ASSERT_EQ(ckp.overprov_segment_count, overprov_segment_count);
+}
+
+TEST(FormatFilesystemTest, MkfsOptionsLabel) {
+  auto device = std::make_unique<FakeBlockDevice>(kBlockCount, kBlockSize);
+  std::unique_ptr<Bcache> bc;
+  bool readonly_device = false;
+  ASSERT_EQ(CreateBcache(std::move(device), &readonly_device, &bc), ZX_OK);
+
+  char label[20];
+  const char *default_label = "F2FS";
+
+  // Check default label is written when there is no arg for label
+  std::vector<const char *> argv = {"mkfs"};
+  DoMkfs(bc.get(), argv, true);
+
+  SuperBlock sb = {};
+  ReadSuperblock(bc.get(), &sb);
+  VerifyLabel(sb, default_label);
+
+  // Try with max size label (16)
+  argv.clear();
+  argv.push_back("mkfs");
+  memcpy(label, "0123456789abcde", 16);
+  AddArg(argv, ArgType::Label, label);
+  DoMkfs(bc.get(), argv, true);
+  ReadSuperblock(bc.get(), &sb);
+  VerifyLabel(sb, label);
+
+  // Check failure with label size larger than max size
+  argv.clear();
+  argv.push_back("mkfs");
+  memcpy(label, "0123456789abcdef", 17);
+  AddArg(argv, ArgType::Label, label);
+  DoMkfs(bc.get(), argv, false);
+}
+
+TEST(FormatFilesystemTest, MkfsOptionsSegsPerSec) {
+  auto device = std::make_unique<FakeBlockDevice>(kBlockCount, kBlockSize);
+  std::unique_ptr<Bcache> bc;
+  bool readonly_device = false;
+  ASSERT_EQ(CreateBcache(std::move(device), &readonly_device, &bc), ZX_OK);
+
+  // Check default value
+  std::vector<const char *> argv = {"mkfs"};
+  DoMkfs(bc.get(), argv, true);
+  SuperBlock sb = {};
+  ReadSuperblock(bc.get(), &sb);
+  VerifySegsPerSec(sb, default_option.segs_per_sec);
+
+  // Try with various values
+  const uint32_t segs_per_sec_list[] = {1, 2, 4, 8};
+  for (uint32_t segs_per_sec : segs_per_sec_list) {
+    std::cout << "segs_per_sec = " << segs_per_sec << std::endl;
+    argv.clear();
+    argv.push_back("mkfs");
+    AddArg(argv, ArgType::SegsPerSec, std::to_string(segs_per_sec).c_str());
+    DoMkfs(bc.get(), argv, true);
+    ReadSuperblock(bc.get(), &sb);
+    VerifySegsPerSec(sb, segs_per_sec);
+  }
+
+  // Check failure with zero
+  argv.clear();
+  argv.push_back("mkfs");
+  AddArg(argv, ArgType::SegsPerSec, "0");
+  DoMkfs(bc.get(), argv, false);
+}
+
+TEST(FormatFilesystemTest, MkfsOptionsSecsPerZone) {
+  auto device = std::make_unique<FakeBlockDevice>(kBlockCount, kBlockSize);
+  std::unique_ptr<Bcache> bc;
+  bool readonly_device = false;
+  ASSERT_EQ(CreateBcache(std::move(device), &readonly_device, &bc), ZX_OK);
+
+  // Check default value
+  std::vector<const char *> argv = {"mkfs"};
+  DoMkfs(bc.get(), argv, true);
+  SuperBlock sb = {};
+  ReadSuperblock(bc.get(), &sb);
+  VerifySecsPerZone(sb, default_option.secs_per_zone);
+
+  // Try with various values
+  const uint32_t secs_per_zone_list[] = {1, 2, 4, 8};
+  for (uint32_t secs_per_zone : secs_per_zone_list) {
+    std::cout << "secs_per_zone = " << secs_per_zone << std::endl;
+    argv.clear();
+    argv.push_back("mkfs");
+    AddArg(argv, ArgType::SecsPerZone, std::to_string(secs_per_zone).c_str());
+    DoMkfs(bc.get(), argv, true);
+    ReadSuperblock(bc.get(), &sb);
+    VerifySecsPerZone(sb, secs_per_zone);
+  }
+
+  // Check failure with zero
+  argv.clear();
+  argv.push_back("mkfs");
+  AddArg(argv, ArgType::SecsPerZone, "0");
+  DoMkfs(bc.get(), argv, false);
+}
+
+TEST(FormatFilesystemTest, MkfsOptionsExtensions) {
+  auto device = std::make_unique<FakeBlockDevice>(kBlockCount, kBlockSize);
+  std::unique_ptr<Bcache> bc;
+  bool readonly_device = false;
+  ASSERT_EQ(CreateBcache(std::move(device), &readonly_device, &bc), ZX_OK);
+
+  // Check default
+  std::vector<const char *> argv = {"mkfs"};
+  DoMkfs(bc.get(), argv, true);
+  SuperBlock sb = {};
+  ReadSuperblock(bc.get(), &sb);
+  VerifyExtensionList(sb, "");
+
+  // Try with max extension counts
+  std::string extensions("");
+  for (uint32_t i = sizeof(kMediaExtList) / sizeof(const char *); i < kMaxExtension; i++) {
+    if (i > sizeof(kMediaExtList) / sizeof(const char *))
+      extensions.append(",");
+    extensions.append(std::to_string(i));
+  }
+  char ext_arg_buf[512];
+  strcpy(ext_arg_buf, extensions.c_str());
+
+  argv.clear();
+  argv.push_back("mkfs");
+  AddArg(argv, ArgType::Extension, ext_arg_buf);
+  DoMkfs(bc.get(), argv, true);
+  ReadSuperblock(bc.get(), &sb);
+  VerifyExtensionList(sb, extensions.c_str());
+
+  // If exeeding max extension counts, only extensions within max count are valid
+  extensions.append(",foo");
+  strcpy(ext_arg_buf, extensions.c_str());
+
+  argv.clear();
+  argv.push_back("mkfs");
+  AddArg(argv, ArgType::Extension, ext_arg_buf);
+  DoMkfs(bc.get(), argv, true);
+  ReadSuperblock(bc.get(), &sb);
+  VerifyExtensionList(sb, extensions.c_str());
+}
+
+TEST(FormatFilesystemTest, MkfsOptionsHeapBasedAlloc) {
+  auto device = std::make_unique<FakeBlockDevice>(kBlockCount, kBlockSize);
+  std::unique_ptr<Bcache> bc;
+  bool readonly_device = false;
+  ASSERT_EQ(CreateBcache(std::move(device), &readonly_device, &bc), ZX_OK);
+
+  // Check default
+  std::vector<const char *> argv = {"mkfs"};
+  DoMkfs(bc.get(), argv, true);
+  SuperBlock sb = {};
+  ReadSuperblock(bc.get(), &sb);
+  Checkpoint ckp = {};
+  ReadCheckpoint(bc.get(), sb, &ckp);
+  VerifyHeapBasedAllocation(sb, ckp, default_option.heap_based_allocation);
+
+  // If arg set to 0, not using heap-based allocation
+  argv.clear();
+  argv.push_back("mkfs");
+  AddArg(argv, ArgType::Heap, "0");
+  DoMkfs(bc.get(), argv, true);
+  ReadSuperblock(bc.get(), &sb);
+  ReadCheckpoint(bc.get(), sb, &ckp);
+  VerifyHeapBasedAllocation(sb, ckp, false);
+
+  // If arg set to 1, using heap-based allocation
+  argv.clear();
+  argv.push_back("mkfs");
+  AddArg(argv, ArgType::Heap, "1");
+  DoMkfs(bc.get(), argv, true);
+  ReadSuperblock(bc.get(), &sb);
+  ReadCheckpoint(bc.get(), sb, &ckp);
+  VerifyHeapBasedAllocation(sb, ckp, true);
+}
+
+TEST(FormatFilesystemTest, MkfsOptionsOverprovision) {
+  auto device = std::make_unique<FakeBlockDevice>(kBlockCount, kBlockSize);
+  std::unique_ptr<Bcache> bc;
+  bool readonly_device = false;
+  ASSERT_EQ(CreateBcache(std::move(device), &readonly_device, &bc), ZX_OK);
+
+  // Check default
+  std::vector<const char *> argv = {"mkfs"};
+  DoMkfs(bc.get(), argv, true);
+  SuperBlock sb = {};
+  ReadSuperblock(bc.get(), &sb);
+  Checkpoint ckp = {};
+  ReadCheckpoint(bc.get(), sb, &ckp);
+  VerifyOP(sb, ckp, default_option.overprovision_ratio);
+
+  // Try with various values
+  const uint32_t overprovision_ratio_list[] = {1, 3, 5};
+  for (uint32_t overprovision_ratio : overprovision_ratio_list) {
+    std::cout << "overprovision_ratio = " << overprovision_ratio << std::endl;
+    argv.clear();
+    argv.push_back("mkfs");
+    AddArg(argv, ArgType::OP, std::to_string(overprovision_ratio).c_str());
+    DoMkfs(bc.get(), argv, true);
+    ReadCheckpoint(bc.get(), sb, &ckp);
+    VerifyOP(sb, ckp, overprovision_ratio);
+  }
+
+  // Check failure with zero
+  argv.clear();
+  argv.push_back("mkfs");
+  AddArg(argv, ArgType::OP, "0");
+  DoMkfs(bc.get(), argv, false);
+}
+
+TEST(FormatFilesystemTest, MkfsOptionsMixed) {
+  auto device = std::make_unique<FakeBlockDevice>(kBlockCount, kBlockSize);
+  std::unique_ptr<Bcache> bc;
+  bool readonly_device = false;
+  ASSERT_EQ(CreateBcache(std::move(device), &readonly_device, &bc), ZX_OK);
+
+  const char *label_list[] = {"aa", "bbbbb"};
+  const uint32_t segs_per_sec_list[] = {2, 4};
+  const uint32_t secs_per_zone_list[] = {2, 4};
+  const char *ext_list[] = {"foo", "foo,bar"};
+  const uint32_t heap_based_list[] = {0};
+  const uint32_t overprovision_list[] = {1, 3};
+
+  for (const char *label : label_list) {
+    for (const uint32_t segs_per_sec : segs_per_sec_list) {
+      for (const uint32_t secs_per_zone : secs_per_zone_list) {
+        for (const char *extensions : ext_list) {
+          for (const uint32_t heap_based : heap_based_list) {
+            for (const uint32_t overprovision : overprovision_list) {
+              std::vector<const char *> argv = {"mkfs"};
+              char ext_arg_buf[512];
+
+              AddArg(argv, ArgType::Label, label);
+              AddArg(argv, ArgType::SegsPerSec, std::to_string(segs_per_sec).c_str());
+              AddArg(argv, ArgType::SecsPerZone, std::to_string(secs_per_zone).c_str());
+
+              // Mkfs will tokenize extension list using strtok().
+              // Since strtok() needs to modify original string, use non-const buffer to deliver
+              // argument
+              strcpy(ext_arg_buf, extensions);
+              AddArg(argv, ArgType::Extension, ext_arg_buf);
+
+              AddArg(argv, ArgType::Heap, std::to_string(heap_based).c_str());
+              AddArg(argv, ArgType::OP, std::to_string(overprovision).c_str());
+
+              PrintArg(argv);
+              DoMkfs(bc.get(), argv, true);
+
+              SuperBlock sb = {};
+              ReadSuperblock(bc.get(), &sb);
+
+              Checkpoint ckp = {};
+              ReadCheckpoint(bc.get(), sb, &ckp);
+
+              VerifyLabel(sb, label);
+              VerifySegsPerSec(sb, segs_per_sec);
+              VerifySecsPerZone(sb, secs_per_zone);
+              VerifyExtensionList(sb, extensions);
+              VerifyHeapBasedAllocation(sb, ckp, (heap_based != 0));
+              VerifyOP(sb, ckp, overprovision);
+            }
+          }
+        }
+      }
+    }
+  }
+}
+
+}  // namespace
+}  // namespace f2fs
diff --git a/tools/main.cc b/tools/main.cc
index 26e1874..b5f22f3 100644
--- a/tools/main.cc
+++ b/tools/main.cc
@@ -25,9 +25,8 @@
 
 #include "third_party/f2fs/f2fs.h"
 
-int main(int argc, const char** argv) {
+int main(int argc, char** argv) {
   f2fs::MountOptions options;
-  f2fs::MkfsOptions mkfs_options;
 
   std::cout << "f2fs arg= ";
 
@@ -82,7 +81,7 @@
 
   // TODO: implement the fsck logic
   if (!strcmp(argv[1], "mkfs")) {
-    f2fs::Mkfs(mkfs_options, std::move(bc));
+    return f2fs::Mkfs(bc.get(), argc, argv);
   } else if (!strcmp(argv[1], "fsck")) {
     f2fs::Fsck(options, std::move(bc));
   } else if (!strcmp(argv[1], "mount")) {