parse-dynparts: list free space on super 70/307270/3
authorJacek Kryszyn <j.kryszyn@samsung.com>
Tue, 20 Feb 2024 15:00:49 +0000 (16:00 +0100)
committerJacek Kryszyn <j.kryszyn@samsung.com>
Wed, 6 Mar 2024 12:36:19 +0000 (13:36 +0100)
Added --free-space flag which allows to list free space on
super partition. Can be combined with --list-tables to print
table as multiple lines. Refactored parse-dynparts a bit so
it doesn't matter in which order --list-tables and --free-space
are given. Filename of super still has to be given as a first
argument to maintain compatibility with existing scripts.

Tests of parse-dynparts were refactored in order to be easier
to maintain. Test data is now created using functions from
liblp instead of external tools and hard to understand
operations on raw structures.

Change-Id: If6446618dae03ff31dc7972c94cc84781dc1b9bb

src/dynamic-partitions/liblp/builder.cpp
src/dynamic-partitions/liblp/include/liblp/builder.h
src/dynamic-partitions/liblp/include/liblp/liblp.h
src/dynamic-partitions/liblp/include/liblp/partition_opener.h [new file with mode: 0644]
src/dynamic-partitions/parse-dynparts/lib.cpp
src/dynamic-partitions/parse-dynparts/lib.hpp
src/dynamic-partitions/parse-dynparts/main.cpp
src/dynamic-partitions/parse-dynparts/test/test.cpp
src/dynamic-partitions/testlib/generate_test_data.sh
src/dynamic-partitions/testlib/metadataio.cpp
src/dynamic-partitions/testlib/metadataio.h

index ac609bf..6743910 100644 (file)
@@ -168,6 +168,16 @@ uint64_t Partition::BytesOnDisk() const {
     return sectors * LP_SECTOR_SIZE;
 }
 
+std::unique_ptr<MetadataBuilder> MetadataBuilder::New(
+        const std::vector<BlockDeviceInfo>& block_devices, const std::string& super_partition,
+        uint32_t metadata_max_size, uint32_t metadata_slot_count) {
+    std::unique_ptr<MetadataBuilder> builder(new MetadataBuilder());
+    if (!builder->Init(block_devices, super_partition, metadata_max_size, metadata_slot_count)) {
+        return nullptr;
+    }
+    return builder;
+}
+
 std::unique_ptr<MetadataBuilder> MetadataBuilder::New(const std::string& super_partition,
                                                       uint32_t slot_number) {
     std::unique_ptr<LpMetadata> metadata = ReadMetadata(super_partition, slot_number);
@@ -245,6 +255,162 @@ void MetadataBuilder::ImportExtents(Partition* dest, const LpMetadata& metadata,
     }
 }
 
+static bool VerifyDeviceProperties(const BlockDeviceInfo& device_info) {
+    if (device_info.logical_block_size == 0) {
+        LERROR << "Block device " << device_info.partition_name
+               << " logical block size must not be zero.";
+        return false;
+    }
+    if (device_info.logical_block_size % LP_SECTOR_SIZE != 0) {
+        LERROR << "Block device " << device_info.partition_name
+               << " logical block size must be a multiple of 512.";
+        return false;
+    }
+    if (device_info.size % device_info.logical_block_size != 0) {
+        LERROR << "Block device " << device_info.partition_name
+               << " size must be a multiple of its block size.";
+        return false;
+    }
+    if (device_info.alignment_offset % LP_SECTOR_SIZE != 0) {
+        LERROR << "Block device " << device_info.partition_name
+               << " alignment offset is not sector-aligned.";
+        return false;
+    }
+    if (device_info.alignment % LP_SECTOR_SIZE != 0) {
+        LERROR << "Block device " << device_info.partition_name
+               << " partition alignment is not sector-aligned.";
+        return false;
+    }
+    return true;
+}
+
+bool MetadataBuilder::Init(const std::vector<BlockDeviceInfo>& block_devices,
+                           const std::string& super_partition, uint32_t metadata_max_size,
+                           uint32_t metadata_slot_count) {
+    if (metadata_max_size < sizeof(LpMetadataHeader)) {
+        LERROR << "Invalid metadata maximum size.";
+        return false;
+    }
+    if (metadata_slot_count == 0) {
+        LERROR << "Invalid metadata slot count.";
+        return false;
+    }
+    if (block_devices.empty()) {
+        LERROR << "No block devices were specified.";
+        return false;
+    }
+
+    // Align the metadata size up to the nearest sector.
+    if (!AlignTo(metadata_max_size, LP_SECTOR_SIZE, &metadata_max_size)) {
+        LERROR << "Max metadata size " << metadata_max_size << " is too large.";
+        return false;
+    }
+
+    // Validate and build the block device list.
+    uint32_t logical_block_size = 0;
+    for (const auto& device_info : block_devices) {
+        if (!VerifyDeviceProperties(device_info)) {
+            return false;
+        }
+
+        if (!logical_block_size) {
+            logical_block_size = device_info.logical_block_size;
+        }
+        if (logical_block_size != device_info.logical_block_size) {
+            LERROR << "All partitions must have the same logical block size.";
+            return false;
+        }
+
+        LpMetadataBlockDevice out = {};
+        out.alignment = device_info.alignment;
+        out.alignment_offset = device_info.alignment_offset;
+        out.size = device_info.size;
+        if (device_info.partition_name.size() > sizeof(out.partition_name)) {
+            LERROR << "Partition name " << device_info.partition_name << " exceeds maximum length.";
+            return false;
+        }
+        strncpy(out.partition_name, device_info.partition_name.c_str(), sizeof(out.partition_name));
+
+        // In the case of the super partition, this field will be adjusted
+        // later. For all partitions, the first 512 bytes are considered
+        // untouched to be compatible code that looks for an MBR. Thus we
+        // start counting free sectors at sector 1, not 0.
+        uint64_t free_area_start = LP_SECTOR_SIZE;
+        bool ok;
+        if (out.alignment) {
+            ok = AlignTo(free_area_start, out.alignment, &free_area_start);
+        } else {
+            ok = AlignTo(free_area_start, logical_block_size, &free_area_start);
+        }
+        if (!ok) {
+            LERROR << "Integer overflow computing free area start";
+            return false;
+        }
+        out.first_logical_sector = free_area_start / LP_SECTOR_SIZE;
+
+        // There must be one logical block of space available.
+        uint64_t minimum_size = out.first_logical_sector * LP_SECTOR_SIZE + logical_block_size;
+        if (device_info.size < minimum_size) {
+            LERROR << "Block device " << device_info.partition_name
+                   << " is too small to hold any logical partitions.";
+            return false;
+        }
+
+        // The "root" of the super partition is always listed first.
+        if (device_info.partition_name == super_partition) {
+            block_devices_.emplace(block_devices_.begin(), out);
+        } else {
+            block_devices_.emplace_back(out);
+        }
+    }
+    if (GetBlockDevicePartitionName(0) != super_partition) {
+        LERROR << "No super partition was specified.";
+        return false;
+    }
+
+    LpMetadataBlockDevice& super = block_devices_[0];
+
+    // We reserve a geometry block (4KB) plus space for each copy of the
+    // maximum size of a metadata blob. Then, we double that space since
+    // we store a backup copy of everything.
+    uint64_t total_reserved = GetTotalMetadataSize(metadata_max_size, metadata_slot_count);
+    if (super.size < total_reserved) {
+        LERROR << "Attempting to create metadata on a block device that is too small.";
+        return false;
+    }
+
+    // Compute the first free sector, factoring in alignment.
+    uint64_t free_area_start = total_reserved;
+    bool ok;
+    if (super.alignment) {
+        ok = AlignTo(free_area_start, super.alignment, &free_area_start);
+    } else {
+        ok = AlignTo(free_area_start, logical_block_size, &free_area_start);
+    }
+    if (!ok) {
+        LERROR << "Integer overflow computing free area start";
+        return false;
+    }
+    super.first_logical_sector = free_area_start / LP_SECTOR_SIZE;
+
+    // There must be one logical block of free space remaining (enough for one partition).
+    uint64_t minimum_disk_size = (super.first_logical_sector * LP_SECTOR_SIZE) + logical_block_size;
+    if (super.size < minimum_disk_size) {
+        LERROR << "Device must be at least " << minimum_disk_size << " bytes, only has "
+               << super.size;
+        return false;
+    }
+
+    geometry_.metadata_max_size = metadata_max_size;
+    geometry_.metadata_slot_count = metadata_slot_count;
+    geometry_.logical_block_size = logical_block_size;
+
+    if (!AddGroup(std::string(kDefaultGroup), 0)) {
+        return false;
+    }
+    return true;
+}
+
 bool MetadataBuilder::AddGroup(std::string_view group_name, uint64_t maximum_size) {
     if (FindGroup(group_name)) {
         LERROR << "Group already exists: " << group_name;
index 7f0bb77..ddfc64e 100644 (file)
@@ -193,6 +193,20 @@ struct Interval {
 
 class MetadataBuilder {
   public:
+    // Construct an empty logical partition table builder given the specified
+    // map of partitions that are available for storing logical partitions.
+    //
+    // At least one partition in the list must be the "super" device, where
+    // metadata will be stored.
+    //
+    // If the parameters would yield invalid metadata, nullptr is returned. This
+    // could happen if the super device is too small to store all required
+    // metadata.
+    static std::unique_ptr<MetadataBuilder> New(const std::vector<BlockDeviceInfo>& block_devices,
+                                                const std::string& super_partition,
+                                                uint32_t metadata_max_size,
+                                                uint32_t metadata_slot_count);
+
     // Import an existing table for modification. This reads metadata off the
     // given block device and imports it. It also adjusts alignment information
     // based on run-time values in the operating system.
@@ -206,6 +220,24 @@ class MetadataBuilder {
     // be updated.
     static std::unique_ptr<MetadataBuilder> New(const LpMetadata& metadata);
 
+    // Helper function for a single super partition, for tests.
+    static std::unique_ptr<MetadataBuilder> New(const BlockDeviceInfo& device_info,
+                                                uint32_t metadata_max_size,
+                                                uint32_t metadata_slot_count) {
+        return New({device_info}, device_info.partition_name, metadata_max_size,
+                   metadata_slot_count);
+    }
+
+    // Wrapper around New() with a BlockDeviceInfo that only specifies a device
+    // size. This is a convenience method for tests.
+    static std::unique_ptr<MetadataBuilder> New(uint64_t blockdev_size, uint32_t metadata_max_size,
+                                                uint32_t metadata_slot_count,
+                                                uint32_t alignment) {
+        BlockDeviceInfo device_info(LP_METADATA_DEFAULT_PARTITION_NAME, blockdev_size, alignment, 0,
+                                    kDefaultBlockSize);
+        return New(device_info, metadata_max_size, metadata_slot_count);
+    }
+
     // Define a new partition group. By default there is one group called
     // "default", with an unrestricted size. A non-zero size will restrict the
     // total space used by all partitions in the group.
@@ -289,6 +321,8 @@ class MetadataBuilder {
     MetadataBuilder& operator=(const MetadataBuilder&) = delete;
     MetadataBuilder& operator=(MetadataBuilder&&) = delete;
     bool Init(const LpMetadata& metadata);
+    bool Init(const std::vector<BlockDeviceInfo>& block_devices, const std::string& super_partition,
+              uint32_t metadata_max_size, uint32_t metadata_slot_count);
     bool GrowPartition(Partition* partition, uint64_t aligned_size,
                        const std::vector<Interval>& free_region_hint);
     void ShrinkPartition(Partition* partition, uint64_t aligned_size);
index c6b9b1b..9a6f6c1 100644 (file)
@@ -25,6 +25,7 @@
 #include <string>
 
 #include "metadata_format.h"
+#include "partition_opener.h"
 
 namespace android {
 namespace fs_mgr {
diff --git a/src/dynamic-partitions/liblp/include/liblp/partition_opener.h b/src/dynamic-partitions/liblp/include/liblp/partition_opener.h
new file mode 100644 (file)
index 0000000..35c9c9a
--- /dev/null
@@ -0,0 +1,52 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#pragma once
+
+#include <stdint.h>
+
+#include <string>
+
+namespace android {
+namespace fs_mgr {
+
+struct BlockDeviceInfo {
+    BlockDeviceInfo() : size(0), alignment(0), alignment_offset(0), logical_block_size(0) {}
+    BlockDeviceInfo(const std::string& partition_name, uint64_t size, uint32_t alignment,
+                    uint32_t alignment_offset, uint32_t logical_block_size)
+        : size(size),
+          alignment(alignment),
+          alignment_offset(alignment_offset),
+          logical_block_size(logical_block_size),
+          partition_name(partition_name) {}
+    // Size of the block device, in bytes.
+    uint64_t size;
+    // Optimal target alignment, in bytes. Partition extents will be aligned to
+    // this value by default. This value must be 0 or a multiple of 512.
+    uint32_t alignment;
+    // Alignment offset to parent device (if any), in bytes. The sector at
+    // |alignment_offset| on the target device is correctly aligned on its
+    // parent device. This value must be 0 or a multiple of 512.
+    uint32_t alignment_offset;
+    // Block size, for aligning extent sizes and partition sizes.
+    uint32_t logical_block_size;
+    // The physical partition name for this block device, as it would appear in
+    // the GPT or under /dev/block/by-name.
+    std::string partition_name;
+};
+
+}  // namespace fs_mgr
+}  // namespace android
index 1981b05..464a315 100644 (file)
@@ -7,6 +7,7 @@
 #include <iostream>
 #include <sstream>
 #include <string_view>
+#include <liblp/builder.h>
 
 using namespace android::fs_mgr;
 using std::endl;
@@ -71,4 +72,40 @@ std::optional<std::string> go(const android::fs_mgr::LpMetadata &metadata, bool
   }
 
   return table;
-}
\ No newline at end of file
+}
+
+std::optional<std::string> go_free(const android::fs_mgr::LpMetadata &metadata, bool list_tables, std::string_view device_name, std::ostream &messages) {
+  auto builder = android::fs_mgr::MetadataBuilder::New(metadata);
+
+  if (!builder) {
+    messages << "Could not create MetadataBuilder" << "\n";
+    return std::nullopt;
+  }
+
+  auto free_regions = builder->GetFreeRegions();
+
+  if (!free_regions.size()) {
+    messages << "No free space on super" << "\n";
+    return std::nullopt;
+  }
+
+  std::ostringstream line;
+
+  if (list_tables)
+    line << "super_free" << " ";
+  else
+    line << "super_free,,,rw,";
+
+  uint64_t sector = 0;
+  for (size_t i = 0; i < free_regions.size(); ++i) {
+    if (i && !list_tables) line << ",";
+    if (i && list_tables) line << "\\n";
+
+    line << sector << " " << free_regions[i].end - free_regions[i].start << " linear "
+         << device_name << " " << free_regions[i].start;
+
+    sector += free_regions[i].end - free_regions[i].start;
+  }
+
+  return line.str();
+}
index 86aa29d..50d7fdd 100644 (file)
@@ -7,4 +7,6 @@
 #include <optional>
 #include <liblp/liblp.h>
 
-std::optional<std::string> go(const android::fs_mgr::LpMetadata &metadata, bool list_tables, std::string_view device_name, std::ostream &messages);
\ No newline at end of file
+std::optional<std::string> go(const android::fs_mgr::LpMetadata &metadata, bool list_tables, std::string_view device_name, std::ostream &messages);
+
+std::optional<std::string> go_free(const android::fs_mgr::LpMetadata &metadata, bool list_tables, std::string_view device_name, std::ostream &messages);
index 35fe9b5..72644e7 100644 (file)
@@ -16,24 +16,49 @@ using std::cerr;
 using std::cout;
 using std::endl;
 
+void usage(char *name) {
+  cerr << "Usage: dmsetup create --concise \"$(" << name << " device)\"" << endl
+       << "Alternatively, you can use --list-tables option to get partitions tables line"
+       << " by line which you can provide to dmsetup create." << endl
+       << "You can also show a table corresponding to free space on super using"
+       << " --free-space option." << endl;
+}
+
 int main(int argc, char* argv[]) {
-  if (argc < 2 || argc > 3 || (argc == 3 && std::string_view(argv[2]).compare("--list-tables"))) {
-    cerr << "Usage: dmsetup create --concise \"$(" << argv[0] << " device)\"" << endl
-         << "Alternatively, you can use --list-tables option to get partitions tables line"
-         << " by line which you can provide to dmsetup create."
-         << endl;
+  if (argc < 2 || argc > 4) {
+    usage(argv[0]);
     return 1;
   }
 
-  bool list_tables = (argc == 3) ? true : false;
+  bool list_tables = false;
+  bool free_space = false;
+
+  for (int i = 2; i < argc; ++i) {
+    if (std::string_view(argv[i]).compare("--list-tables") == 0) {
+      list_tables = true;
+    } else if (std::string_view(argv[i]).compare("--free-space") == 0) {
+      free_space = true;
+    } else {
+      usage(argv[0]);
+      return 1;
+    }
+  }
 
   auto metadata = ReadMetadata(argv[1], 0);
+
   if (!metadata) {
     cerr << "Failed to parse metadata from \"" << argv[1] << "\"" << endl;
     return 1;
   }
 
-  auto out = go(*metadata, list_tables, argv[1], cerr);
+  std::optional<std::string> out;
+
+  if (free_space) {
+    out = go_free(*metadata, list_tables, argv[1], cerr);
+  } else {
+    out = go(*metadata, list_tables, argv[1], cerr);
+  }
+
   if (out)
     cout << *out << "\n";
   else
index 4b356dd..0bea05e 100644 (file)
@@ -1,19 +1,27 @@
 /* Copyright (c) 2023 Samsung Electronics Co., Ltd.
  * SPDX-License-Identifier: MIT
  *
- * parse-dynparts relies only on LpMetadata.partitions and LpMetadata.extents
- * fields. That's why it is only needed to fill these specific fields of an
- * empty LpMetadata structure in order to generate output for tests.
+ * Two different methods of generating data were used in below tests. Sometimes
+ * they give different results. From the logical point of view generated
+ * metadata is identical, but maps of partitions may differ.
  *
- * To avoid any errors due to manual creation of LpMetadata, lpmake was used
- * to create real super images having certain partition and group layouts.
- * Next, values of certain fields were read in a debugger and used to create
- * test LpMetadata structures.
+ * The first method relies on lpmake and lpadd tools to create real super
+ * images having certain partition and group layouts. Next, super_dump
+ * tool was used to generate metadata_str which contains full dump of a super
+ * image using stream operators from metadataio.h. Finally, metadata is
+ * reconstructed from this string using another set of metadataio.h operators.
  *
- * Each test contains metadata_str variable which contains full dump of a
- * super image created using stream operators from metadataio.h. This is done
- * to allow creation of a full LpMetadata structure which can be used to create
- * real super image corresponding to a specific test. */
+ * The second method uses liblp functions to create mock metadata in a clear
+ * and programmatic way which doesn't require any external tools and additional
+ * efforts. This is the default for all tests.
+ *
+ * Both methods differ because lpmake/lpadd tools used to generate super images
+ * were built using external open-source project whereas the second method uses
+ * liblp from this project. Both projects differ in values returned by
+ * android::fs_mgr::MetadataBuilder::IsABDevice() and
+ * android::fs_mgr::MetadataBuilder::ShouldHalveSuper() which results in different
+ * approach to how partitions are layed out in super.
+ */
 
 #include <iostream>
 #include <gtest/gtest.h>
@@ -21,6 +29,9 @@
 #include "../lib.hpp"
 #include "../../testlib/metadataio.h"
 
+#include <liblp/liblp.h>
+#include <liblp/builder.h>
+
 using namespace android::fs_mgr;
 
 // Empty metadata
@@ -30,20 +41,35 @@ TEST(ParseDynpartsTest, EmptyMetadata) {
     EXPECT_EQ(go(metadata, false, "test", messages), "");
     EXPECT_EQ(messages.str(), "");
 
+    messages.str("");
+    messages.clear();
+
     EXPECT_EQ(go(metadata, true, "test", messages), "");
     EXPECT_EQ(messages.str(), "");
+
+    messages.str("");
+    messages.clear();
+
+    EXPECT_EQ(go_free(metadata, false, "test", messages), std::nullopt);
+    EXPECT_EQ(messages.str(), "No free space on super\n");
+
+    messages.str("");
+    messages.clear();
+
+    EXPECT_EQ(go_free(metadata, true, "test", messages), std::nullopt);
+    EXPECT_EQ(messages.str(), "No free space on super\n");
 }
 
 // One group and one partition
 TEST(ParseDynpartsTest, GroupAOnePart) {
-    LpMetadata metadata;
-
 #ifdef METADATA_IO
     // super_1.img
     std::string metadata_str = "1634485351 52 65536 1 4096 120 75 47 222 48 213 6 78 122 230 66 214 51 226 131 122 78 100 23 86 88 86 36 222 17 7 236 187 122 127 108 9  1095520304 10 0 128 236 0 0 1 52  52 1 24  76 2 48  172 1 64  79 119 146 123 183 233 167 221 228 70 230 58 47 182 200 165 64 233 92 109 201 251 116 215 255 12 1 196 85 200 104 222 247 121 40 1 114 204 108 169 136 120 22 181 181 235 50 96 48 125 91 42 101 198 44 159 40 40 172 246 180 45 102 172 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  1 0 0 1 1 112 97 114 116 95 49 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  1 10240 0 2048 0  2 0 0 100 101 102 97 117 108 116 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 5242880 103 114 111 117 112 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  1 2048 1048576 0 6291456 0 115 117 112 101 114 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0";
+    android::fs_mgr::LpMetadata metadata;
     metadataio::readMetadata(metadata, metadata_str);
 #else
-   addPartitions(metadata, 1, 1);
+    auto metadata_ptr = addPartitions(1, 1);
+    android::fs_mgr::LpMetadata &metadata = *metadata_ptr;
 #endif
 
     std::string output = "part_1_a,,,rw,0 10240 linear test 2048";
@@ -53,20 +79,35 @@ TEST(ParseDynpartsTest, GroupAOnePart) {
     EXPECT_EQ(go(metadata, false, "test", messages), output);
     EXPECT_EQ(messages.str(), "");
 
+    messages.str("");
+    messages.clear();
+
     EXPECT_EQ(go(metadata, true, "test", messages), output_list_tables);
     EXPECT_EQ(messages.str(), "");
+
+    messages.str("");
+    messages.clear();
+
+    EXPECT_EQ(go_free(metadata, false, "test", messages), std::nullopt);
+    EXPECT_EQ(messages.str(), "No free space on super\n");
+
+    messages.str("");
+    messages.clear();
+
+    EXPECT_EQ(go_free(metadata, true, "test", messages), std::nullopt);
+    EXPECT_EQ(messages.str(), "No free space on super\n");
 }
 
 // One group and two partitions
 TEST(ParseDynpartsTest, GroupATwoParts) {
-    LpMetadata metadata {};
-
 #ifdef METADATA_IO
     // super_2.img
     std::string metadata_str = "1634485351 52 65536 1 4096 120 75 47 222 48 213 6 78 122 230 66 214 51 226 131 122 78 100 23 86 88 86 36 222 17 7 236 187 122 127 108 9  1095520304 10 0 128 312 0 0 2 52  104 2 24  152 2 48  248 1 64  234 50 126 183 222 226 91 93 28 243 169 49 212 81 150 149 179 92 156 241 115 36 233 141 148 131 230 125 185 16 247 227 246 93 123 112 111 101 135 103 119 224 141 134 251 57 64 128 15 228 200 224 70 34 13 182 239 30 119 67 54 53 127 102 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  2 0 0 1 1 112 97 114 116 95 49 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 1 1 1 112 97 114 116 95 50 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  2 10240 0 2048 0  10240 0 12288 0  2 0 0 100 101 102 97 117 108 116 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 10485760 103 114 111 117 112 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  1 2048 1048576 0 11534336 0 115 117 112 101 114 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  ";
+    LpMetadata metadata {};
     metadataio::readMetadata(metadata, metadata_str);
 #else
-    addPartitions(metadata, 1, 2);
+    auto metadata_ptr = addPartitions(1, 2);
+    android::fs_mgr::LpMetadata &metadata = *metadata_ptr;
 #endif
 
     std::string output = "part_1_a,,,rw,0 10240 linear test 2048;part_2_a,,,rw,0 10240 linear test 12288";
@@ -76,20 +117,35 @@ TEST(ParseDynpartsTest, GroupATwoParts) {
     EXPECT_EQ(go(metadata, false, "test", messages), output);
     EXPECT_EQ(messages.str(), "");
 
+    messages.str("");
+    messages.clear();
+
     EXPECT_EQ(go(metadata, true, "test", messages), output_list_tables);
     EXPECT_EQ(messages.str(), "");
+
+    messages.str("");
+    messages.clear();
+
+    EXPECT_EQ(go_free(metadata, false, "test", messages), std::nullopt);
+    EXPECT_EQ(messages.str(), "No free space on super\n");
+
+    messages.str("");
+    messages.clear();
+
+    EXPECT_EQ(go_free(metadata, true, "test", messages), std::nullopt);
+    EXPECT_EQ(messages.str(), "No free space on super\n");
 }
 
 // One group with three partitions
 TEST(ParseDynpartsTest, GroupAThreeParts) {
-    LpMetadata metadata {};
-
 #ifdef METADATA_IO
     // super_3.img
     std::string metadata_str = "1634485351 52 65536 1 4096 120 75 47 222 48 213 6 78 122 230 66 214 51 226 131 122 78 100 23 86 88 86 36 222 17 7 236 187 122 127 108 9  1095520304 10 0 128 388 0 0 3 52  156 3 24  228 2 48  324 1 64  50 128 136 94 112 62 194 47 58 27 254 167 231 39 117 233 120 245 153 17 230 74 195 158 219 181 195 22 245 118 29 145 63 94 243 41 255 150 90 252 99 109 75 92 55 74 9 197 186 247 143 19 32 184 219 58 97 101 37 225 189 243 133 112 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  3 0 0 1 1 112 97 114 116 95 49 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 1 1 1 112 97 114 116 95 50 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 2 1 1 112 97 114 116 95 51 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  3 10240 0 2048 0  10240 0 12288 0  10240 0 22528 0  2 0 0 100 101 102 97 117 108 116 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 15728640 103 114 111 117 112 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  1 2048 1048576 0 16777216 0 115 117 112 101 114 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  ";
+    LpMetadata metadata {};
     metadataio::readMetadata(metadata, metadata_str);
 #else
-    addPartitions(metadata, 1, 3);
+    auto metadata_ptr = addPartitions(1, 3);
+    android::fs_mgr::LpMetadata &metadata = *metadata_ptr;
 #endif
 
     std::string output = "part_1_a,,,rw,0 10240 linear test 2048;part_2_a,,,rw,0 10240 linear test 12288;part_3_a,,,rw,0 10240 linear test 22528";
@@ -99,20 +155,35 @@ TEST(ParseDynpartsTest, GroupAThreeParts) {
     EXPECT_EQ(go(metadata, false, "test", messages), output);
     EXPECT_EQ(messages.str(), "");
 
+    messages.str("");
+    messages.clear();
+
     EXPECT_EQ(go(metadata, true, "test", messages), output_list_tables);
     EXPECT_EQ(messages.str(), "");
+
+    messages.str("");
+    messages.clear();
+
+    EXPECT_EQ(go_free(metadata, false, "test", messages), std::nullopt);
+    EXPECT_EQ(messages.str(), "No free space on super\n");
+
+    messages.str("");
+    messages.clear();
+
+    EXPECT_EQ(go_free(metadata, true, "test", messages), std::nullopt);
+    EXPECT_EQ(messages.str(), "No free space on super\n");
 }
 
 // One group with four partitions
 TEST(ParseDynpartsTest, GroupAFourParts) {
-    LpMetadata metadata {};
-
 #ifdef METADATA_IO
     // super_4.img
     std::string metadata_str = "1634485351 52 65536 1 4096 120 75 47 222 48 213 6 78 122 230 66 214 51 226 131 122 78 100 23 86 88 86 36 222 17 7 236 187 122 127 108 9  1095520304 10 0 128 464 0 0 4 52  208 4 24  304 2 48  400 1 64  155 220 87 185 23 251 126 142 219 228 253 211 79 90 28 224 57 126 231 190 248 223 25 225 72 199 59 106 57 205 60 213 118 78 28 69 115 211 73 136 129 190 152 180 159 235 110 226 196 144 220 68 5 94 91 193 145 199 98 31 44 84 122 76 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  4 0 0 1 1 112 97 114 116 95 49 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 1 1 1 112 97 114 116 95 50 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 2 1 1 112 97 114 116 95 51 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 3 1 1 112 97 114 116 95 52 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  4 10240 0 2048 0  10240 0 12288 0  10240 0 22528 0  10240 0 32768 0  2 0 0 100 101 102 97 117 108 116 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 20971520 103 114 111 117 112 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  1 2048 1048576 0 22020096 0 115 117 112 101 114 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0";
+    LpMetadata metadata {};
     metadataio::readMetadata(metadata, metadata_str);
 #else
-    addPartitions(metadata, 1, 4);
+    auto metadata_ptr = addPartitions(1, 4);
+    android::fs_mgr::LpMetadata &metadata = *metadata_ptr;
 #endif
 
     std::string output = "part_1_a,,,rw,0 10240 linear test 2048;part_2_a,,,rw,0 10240 linear test 12288;part_3_a,,,rw,0 10240 linear test 22528;part_4_a,,,rw,0 10240 linear test 32768";
@@ -122,20 +193,35 @@ TEST(ParseDynpartsTest, GroupAFourParts) {
     EXPECT_EQ(go(metadata, false, "test", messages), output);
     EXPECT_EQ(messages.str(), "");
 
+    messages.str("");
+    messages.clear();
+
     EXPECT_EQ(go(metadata, true, "test", messages), output_list_tables);
     EXPECT_EQ(messages.str(), "");
+
+    messages.str("");
+    messages.clear();
+
+    EXPECT_EQ(go_free(metadata, false, "test", messages), std::nullopt);
+    EXPECT_EQ(messages.str(), "No free space on super\n");
+
+    messages.str("");
+    messages.clear();
+
+    EXPECT_EQ(go_free(metadata, true, "test", messages), std::nullopt);
+    EXPECT_EQ(messages.str(), "No free space on super\n");
 }
 
 // Two groups with one partition each
 TEST(ParseDynpartsTest, GroupAOnePartGroupBOnePart) {
-    LpMetadata metadata {};
-
 #ifdef METADATA_IO
     // super_5.img
     std::string metadata_str = "1634485351 52 65536 2 4096 78 49 207 100 39 84 66 244 14 37 199 114 161 141 31 204 216 177 41 18 50 229 147 246 94 82 47 199 172 7 223 3  1095520304 10 0 128 360 0 0 2 52  104 2 24  152 3 48  296 1 64  142 38 219 208 156 164 215 68 103 134 165 40 64 23 126 197 223 167 68 151 23 207 114 219 153 32 233 95 156 6 124 138 204 6 90 104 78 237 218 3 93 171 2 26 199 239 20 254 34 135 133 12 208 241 49 201 50 111 119 119 142 110 16 192 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  2 0 0 1 1 112 97 114 116 95 49 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 1 1 2 112 97 114 116 95 49 95 98 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  2 10240 0 2048 0  10240 0 12288 0  3 0 0 100 101 102 97 117 108 116 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 5242880 103 114 111 117 112 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 5242880 103 114 111 117 112 95 98 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  1 2048 1048576 0 11534336 0 115 117 112 101 114 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0";
+    LpMetadata metadata {};
     metadataio::readMetadata(metadata, metadata_str);
 #else
-    addPartitions(metadata, 2, 1);
+    auto metadata_ptr = addPartitions(2, 1);
+    android::fs_mgr::LpMetadata &metadata = *metadata_ptr;
 #endif
 
     std::string output = "part_1_a,,,rw,0 10240 linear test 2048;part_1_b,,,rw,0 10240 linear test 12288";
@@ -145,20 +231,35 @@ TEST(ParseDynpartsTest, GroupAOnePartGroupBOnePart) {
     EXPECT_EQ(go(metadata, false, "test", messages), output);
     EXPECT_EQ(messages.str(), "");
 
+    messages.str("");
+    messages.clear();
+
     EXPECT_EQ(go(metadata, true, "test", messages), output_list_tables);
     EXPECT_EQ(messages.str(), "");
+
+    messages.str("");
+    messages.clear();
+
+    EXPECT_EQ(go_free(metadata, false, "test", messages), std::nullopt);
+    EXPECT_EQ(messages.str(), "No free space on super\n");
+
+    messages.str("");
+    messages.clear();
+
+    EXPECT_EQ(go_free(metadata, true, "test", messages), std::nullopt);
+    EXPECT_EQ(messages.str(), "No free space on super\n");
 }
 
 // Two groups with two partitions each
 TEST(ParseDynpartsTest, GroupATwoPartsGroupBTwoParts) {
-    LpMetadata metadata {};
-
 #ifdef METADATA_IO
     // super_6.img
     std::string metadata_str = "1634485351 52 65536 2 4096 78 49 207 100 39 84 66 244 14 37 199 114 161 141 31 204 216 177 41 18 50 229 147 246 94 82 47 199 172 7 223 3  1095520304 10 0 128 512 0 0 4 52  208 4 24  304 3 48  448 1 64  161 234 225 151 214 94 179 110 2 0 233 234 4 104 28 117 218 103 195 188 66 118 247 36 180 140 15 130 86 252 67 97 115 0 139 231 64 64 16 79 80 219 28 134 12 81 237 114 150 253 218 198 222 11 73 134 255 98 56 133 250 245 232 168 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  4 0 0 1 1 112 97 114 116 95 49 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 1 1 1 112 97 114 116 95 50 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 2 1 2 112 97 114 116 95 49 95 98 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 3 1 2 112 97 114 116 95 50 95 98 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  4 10240 0 2048 0  10240 0 12288 0  10240 0 22528 0  10240 0 32768 0  3 0 0 100 101 102 97 117 108 116 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 10485760 103 114 111 117 112 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 10485760 103 114 111 117 112 95 98 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  1 2048 1048576 0 22020096 0 115 117 112 101 114 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0";
+    LpMetadata metadata {};
     metadataio::readMetadata(metadata, metadata_str);
 #else
-    addPartitions(metadata, 2, 2);
+    auto metadata_ptr = addPartitions(2, 2);
+    android::fs_mgr::LpMetadata &metadata = *metadata_ptr;
 #endif
 
     std::string output = "part_1_a,,,rw,0 10240 linear test 2048;part_2_a,,,rw,0 10240 linear test 12288;part_1_b,,,rw,0 10240 linear test 22528;part_2_b,,,rw,0 10240 linear test 32768";
@@ -168,20 +269,35 @@ TEST(ParseDynpartsTest, GroupATwoPartsGroupBTwoParts) {
     EXPECT_EQ(go(metadata, false, "test", messages), output);
     EXPECT_EQ(messages.str(), "");
 
+    messages.str("");
+    messages.clear();
+
     EXPECT_EQ(go(metadata, true, "test", messages), output_list_tables);
     EXPECT_EQ(messages.str(), "");
+
+    messages.str("");
+    messages.clear();
+
+    EXPECT_EQ(go_free(metadata, false, "test", messages), std::nullopt);
+    EXPECT_EQ(messages.str(), "No free space on super\n");
+
+    messages.str("");
+    messages.clear();
+
+    EXPECT_EQ(go_free(metadata, true, "test", messages), std::nullopt);
+    EXPECT_EQ(messages.str(), "No free space on super\n");
 }
 
 // Two groups with three partitions each
 TEST(ParseDynpartsTest, GroupAThreePartsGroupBThreeParts) {
-    LpMetadata metadata {};
-
 #ifdef METADATA_IO
     // super_7.img
     std::string metadata_str = "1634485351 52 65536 2 4096 78 49 207 100 39 84 66 244 14 37 199 114 161 141 31 204 216 177 41 18 50 229 147 246 94 82 47 199 172 7 223 3  1095520304 10 0 128 664 0 0 6 52  312 6 24  456 3 48  600 1 64  69 42 160 36 106 231 230 54 104 182 243 64 70 203 84 13 147 38 249 74 127 205 226 59 138 82 57 100 86 160 64 74 211 56 108 6 122 167 23 172 28 44 13 73 223 76 115 107 5 151 86 171 104 128 132 48 208 174 108 126 12 62 205 69 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  6 0 0 1 1 112 97 114 116 95 49 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 1 1 1 112 97 114 116 95 50 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 2 1 1 112 97 114 116 95 51 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 3 1 2 112 97 114 116 95 49 95 98 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 4 1 2 112 97 114 116 95 50 95 98 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 5 1 2 112 97 114 116 95 51 95 98 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  6 10240 0 2048 0  10240 0 12288 0  10240 0 22528 0  10240 0 32768 0  10240 0 43008 0  10240 0 53248 0  3 0 0 100 101 102 97 117 108 116 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 15728640 103 114 111 117 112 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 15728640 103 114 111 117 112 95 98 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  1 2048 1048576 0 32505856 0 115 117 112 101 114 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0";
+    LpMetadata metadata {};
     metadataio::readMetadata(metadata, metadata_str);
 #else
-    addPartitions(metadata, 2, 3);
+    auto metadata_ptr = addPartitions(2, 3);
+    android::fs_mgr::LpMetadata &metadata = *metadata_ptr;
 #endif
 
     std::string output = "part_1_a,,,rw,0 10240 linear test 2048;part_2_a,,,rw,0 10240 linear test 12288;part_3_a,,,rw,0 10240 linear test 22528;part_1_b,,,rw,0 10240 linear test 32768;part_2_b,,,rw,0 10240 linear test 43008;part_3_b,,,rw,0 10240 linear test 53248";
@@ -191,20 +307,35 @@ TEST(ParseDynpartsTest, GroupAThreePartsGroupBThreeParts) {
     EXPECT_EQ(go(metadata, false, "test", messages), output);
     EXPECT_EQ(messages.str(), "");
 
+    messages.str("");
+    messages.clear();
+
     EXPECT_EQ(go(metadata, true, "test", messages), output_list_tables);
     EXPECT_EQ(messages.str(), "");
+
+    messages.str("");
+    messages.clear();
+
+    EXPECT_EQ(go_free(metadata, false, "test", messages), std::nullopt);
+    EXPECT_EQ(messages.str(), "No free space on super\n");
+
+    messages.str("");
+    messages.clear();
+
+    EXPECT_EQ(go_free(metadata, true, "test", messages), std::nullopt);
+    EXPECT_EQ(messages.str(), "No free space on super\n");
 }
 
 // Two groups with four partitions each
 TEST(ParseDynpartsTest, GroupsAFourPartsGroupBFourParts) {
-    LpMetadata metadata {};
-
 #ifdef METADATA_IO
     // super_8.img
     std::string metadata_str = "1634485351 52 65536 2 4096 78 49 207 100 39 84 66 244 14 37 199 114 161 141 31 204 216 177 41 18 50 229 147 246 94 82 47 199 172 7 223 3  1095520304 10 0 128 816 0 0 8 52  416 8 24  608 3 48  752 1 64  127 63 109 36 54 5 233 190 2 129 37 201 27 54 232 17 130 172 20 225 202 21 214 104 177 90 5 77 19 153 180 250 211 202 212 140 38 22 156 156 220 142 213 188 92 55 152 201 86 69 186 196 197 69 88 178 91 117 218 245 88 31 107 143 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  8 0 0 1 1 112 97 114 116 95 49 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 1 1 1 112 97 114 116 95 50 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 2 1 1 112 97 114 116 95 51 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 3 1 1 112 97 114 116 95 52 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 4 1 2 112 97 114 116 95 49 95 98 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 5 1 2 112 97 114 116 95 50 95 98 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 6 1 2 112 97 114 116 95 51 95 98 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 7 1 2 112 97 114 116 95 52 95 98 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  8 10240 0 2048 0  10240 0 12288 0  10240 0 22528 0  10240 0 32768 0  10240 0 43008 0  10240 0 53248 0  10240 0 63488 0  10240 0 73728 0  3 0 0 100 101 102 97 117 108 116 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 20971520 103 114 111 117 112 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 20971520 103 114 111 117 112 95 98 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  1 2048 1048576 0 42991616 0 115 117 112 101 114 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0";
+    LpMetadata metadata {};
     metadataio::readMetadata(metadata, metadata_str);
 #else
-    addPartitions(metadata, 2, 4);
+    auto metadata_ptr = addPartitions(2, 4);
+    android::fs_mgr::LpMetadata &metadata = *metadata_ptr;
 #endif
 
     std::string output = "part_1_a,,,rw,0 10240 linear test 2048;part_2_a,,,rw,0 10240 linear test 12288;part_3_a,,,rw,0 10240 linear test 22528;part_4_a,,,rw,0 10240 linear test 32768;part_1_b,,,rw,0 10240 linear test 43008;part_2_b,,,rw,0 10240 linear test 53248;part_3_b,,,rw,0 10240 linear test 63488;part_4_b,,,rw,0 10240 linear test 73728";
@@ -214,684 +345,613 @@ TEST(ParseDynpartsTest, GroupsAFourPartsGroupBFourParts) {
     EXPECT_EQ(go(metadata, false, "test", messages), output);
     EXPECT_EQ(messages.str(), "");
 
+    messages.str("");
+    messages.clear();
+
     EXPECT_EQ(go(metadata, true, "test", messages), output_list_tables);
     EXPECT_EQ(messages.str(), "");
+
+    messages.str("");
+    messages.clear();
+
+    EXPECT_EQ(go_free(metadata, false, "test", messages), std::nullopt);
+    EXPECT_EQ(messages.str(), "No free space on super\n");
+
+    messages.str("");
+    messages.clear();
+
+    EXPECT_EQ(go_free(metadata, true, "test", messages), std::nullopt);
+    EXPECT_EQ(messages.str(), "No free space on super\n");
 }
 
 // One group with four partitions; one partition consists of two extents
 TEST(ParseDynpartsTest, GroupAFourPartsOneHasTwoExtents) {
-    LpMetadata metadata {};
-
 #ifdef METADATA_IO
     // super_partitioned_1.img based on super_3.img
     std::string metadata_str = "1634485351 52 65536 1 4096 120 75 47 222 48 213 6 78 122 230 66 214 51 226 131 122 78 100 23 86 88 86 36 222 17 7 236 187 122 127 108 9  1095520304 10 0 128 488 0 0 4 52  208 5 24  328 2 48  424 1 64  182 115 169 159 30 122 130 132 245 100 53 140 175 182 14 165 119 221 226 206 237 134 81 0 178 176 36 162 210 243 169 188 97 79 116 70 101 55 92 119 186 140 75 200 30 166 205 45 223 0 110 5 194 237 47 150 98 82 236 182 154 161 98 31 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  4 0 0 1 1 112 97 114 116 95 50 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 1 1 1 112 97 114 116 95 49 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 2 1 1 112 97 114 116 95 51 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 3 2 1 112 97 114 116 95 52 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  5 10240 0 12288 0  2048 0 2048 0  2048 0 4096 0  6144 0 6144 0  4096 0 22528 0  2 0 0 100 101 102 97 117 108 116 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 15728640 103 114 111 117 112 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  1 2048 1048576 0 16777216 0 115 117 112 101 114 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0";
+    LpMetadata metadata {};
     metadataio::readMetadata(metadata, metadata_str);
 #else
-    addPartitions(metadata, 1, 4);
-
-    strncpy(metadata.partitions[0].name, "part_2_a", 36);
-
-    strncpy(metadata.partitions[1].name, "part_1_a", 36);
-
-    metadata.partitions[3].num_extents = 2;
+    auto metadata_ptr = addPartitions(1, 3);
+    auto builder = android::fs_mgr::MetadataBuilder::New(*metadata_ptr);
 
-    metadata.extents[0].target_data = 12288;
+    builder->RemovePartition("part_1_a");
+    auto part = builder->AddPartition("part_1_a", "group_a", LP_PARTITION_ATTR_NONE);
+    EXPECT_NE(part, nullptr);
+    EXPECT_EQ(builder->ResizePartition(part, 1024 * 1024), true);
 
-    metadata.extents[1].num_sectors = 2048;
-    metadata.extents[1].target_data = 2048;
+    builder->RemovePartition("part_3_a");
+    part = builder->AddPartition("part_3_a", "group_a", LP_PARTITION_ATTR_NONE);
+    EXPECT_NE(part, nullptr);
+    EXPECT_EQ(builder->ResizePartition(part, 1024 * 1024), true);
 
-    metadata.extents[2].num_sectors = 2048;
-    metadata.extents[2].target_data = 4096;
+    part = builder->AddPartition("part_4_a", "group_a", LP_PARTITION_ATTR_NONE);
+    EXPECT_NE(part, nullptr);
+    EXPECT_EQ(builder->ResizePartition(part, 5 * 1024 * 1024), true);
 
-    metadata.extents[3].num_sectors = 6144;
-    metadata.extents[3].target_data = 6144;
-
-    LpMetadataExtent e;
-    e.num_sectors = 4096;
-    e.target_type = 0;
-    e.target_data = 22528;
-    e.target_source = 0;
-
-    metadata.extents.push_back(e);
+    metadata_ptr = builder->Export();
+    LpMetadata &metadata = *metadata_ptr;
 #endif
 
     std::string output = "part_2_a,,,rw,0 10240 linear test 12288;part_1_a,,,rw,0 2048 linear test 2048;part_3_a,,,rw,0 2048 linear test 4096;part_4_a,,,rw,0 6144 linear test 6144,6144 4096 linear test 22528";
     std::string output_list_tables = "part_2_a 0 10240 linear test 12288\npart_1_a 0 2048 linear test 2048\npart_3_a 0 2048 linear test 4096\npart_4_a 0 6144 linear test 6144\\n6144 4096 linear test 22528";
+    std::string output_free = "super_free,,,rw,0 6144 linear test 26624";
+    std::string output_free_list_tables = "super_free 0 6144 linear test 26624";
     std::ostringstream messages;
 
     EXPECT_EQ(go(metadata, false, "test", messages), output);
     EXPECT_EQ(messages.str(), "");
 
+    messages.str("");
+    messages.clear();
+
     EXPECT_EQ(go(metadata, true, "test", messages), output_list_tables);
     EXPECT_EQ(messages.str(), "");
+
+    messages.str("");
+    messages.clear();
+
+    EXPECT_EQ(go_free(metadata, false, "test", messages), output_free);
+    EXPECT_EQ(messages.str(), "");
+
+    messages.str("");
+    messages.clear();
+
+    EXPECT_EQ(go_free(metadata, true, "test", messages), output_free_list_tables);
+    EXPECT_EQ(messages.str(), "");
 }
 
 // One group with five partitions; two partitions consist of two extents
 TEST(ParseDynpartsTest, GroupAFivePartsTwoHaveTwoExtents) {
-    LpMetadata metadata {};
-
 #ifdef METADATA_IO
     // super_partitioned_2.img based on super_3.img
     std::string metadata_str = "1634485351 52 65536 1 4096 120 75 47 222 48 213 6 78 122 230 66 214 51 226 131 122 78 100 23 86 88 86 36 222 17 7 236 187 122 127 108 9  1095520304 10 0 128 588 0 0 5 52  260 7 24  428 2 48  524 1 64  112 102 57 102 31 202 124 74 55 54 2 70 130 54 95 99 236 209 243 70 173 136 30 29 243 29 206 174 24 109 164 35 93 117 80 179 206 178 79 53 134 247 15 155 144 9 80 221 184 44 89 81 235 112 227 147 157 150 115 117 199 14 210 87 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  5 0 0 1 1 112 97 114 116 95 49 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 1 1 1 112 97 114 116 95 51 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 2 2 1 112 97 114 116 95 52 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 4 1 1 112 97 114 116 95 50 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 5 2 1 112 97 114 116 95 53 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  7 2048 0 2048 0  2048 0 4096 0  6144 0 6144 0  4096 0 22528 0  2048 0 12288 0  8192 0 14336 0  2048 0 26624 0  2 0 0 100 101 102 97 117 108 116 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 15728640 103 114 111 117 112 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  1 2048 1048576 0 16777216 0 115 117 112 101 114 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0";
+        LpMetadata metadata {};
     metadataio::readMetadata(metadata, metadata_str);
 #else
-    addPartitions(metadata, 1, 5);
-
-    strncpy(metadata.partitions[1].name, "part_3_a", 36);
-
-    strncpy(metadata.partitions[2].name, "part_4_a", 36);
-    metadata.partitions[2].num_extents = 2;
-
-    strncpy(metadata.partitions[3].name, "part_2_a", 36);
-    metadata.partitions[3].first_extent_index = 4;
-
-    metadata.partitions[4].first_extent_index = 5;
-    metadata.partitions[4].num_extents = 2;
-
-    metadata.extents[0].num_sectors = 2048;
-    metadata.extents[0].target_data = 2048;
-
-    metadata.extents[1].num_sectors = 2048;
-    metadata.extents[1].target_data = 4096;
-
-    metadata.extents[2].num_sectors = 6144;
-    metadata.extents[2].target_data = 6144;
-
-    metadata.extents[3].num_sectors = 4096;
-    metadata.extents[3].target_data = 22528;
-
-    metadata.extents[4].num_sectors = 2048;
-    metadata.extents[4].target_data = 12288;
-
-    LpMetadataExtent e;
-    e.num_sectors = 8192;
-    e.target_type = 0;
-    e.target_data = 14336;
-    e.target_source = 0;
-
-    metadata.extents.push_back(e);
-
-    e.num_sectors = 2048;
-    e.target_type = 0;
-    e.target_data = 26624;
-    e.target_source = 0;
-
-    metadata.extents.push_back(e);
+    auto metadata_ptr = addPartitions(1, 3);
+    auto builder = android::fs_mgr::MetadataBuilder::New(*metadata_ptr);
+
+    builder->RemovePartition("part_1_a");
+    auto part = builder->AddPartition("part_1_a", "group_a", LP_PARTITION_ATTR_NONE);
+    EXPECT_NE(part, nullptr);
+    EXPECT_EQ(builder->ResizePartition(part, 1024 * 1024), true);
+
+    builder->RemovePartition("part_3_a");
+    part = builder->AddPartition("part_3_a", "group_a", LP_PARTITION_ATTR_NONE);
+    EXPECT_NE(part, nullptr);
+    EXPECT_EQ(builder->ResizePartition(part, 1024 * 1024), true);
+
+    part = builder->AddPartition("part_4_a", "group_a", LP_PARTITION_ATTR_NONE);
+    EXPECT_NE(part, nullptr);
+    EXPECT_EQ(builder->ResizePartition(part, 5 * 1024 * 1024), true);
+
+    builder->RemovePartition("part_2_a");
+    part = builder->AddPartition("part_2_a", "group_a", LP_PARTITION_ATTR_NONE);
+    EXPECT_NE(part, nullptr);
+    EXPECT_EQ(builder->ResizePartition(part, 1024 * 1024), true);
+
+    part = builder->AddPartition("part_5_a", "group_a", LP_PARTITION_ATTR_NONE);
+    EXPECT_NE(part, nullptr);
+    EXPECT_EQ(builder->ResizePartition(part, 5 * 1024 * 1024), true);
+
+    metadata_ptr = builder->Export();
+    LpMetadata &metadata = *metadata_ptr;
 #endif
 
     std::string output = "part_1_a,,,rw,0 2048 linear test 2048;part_3_a,,,rw,0 2048 linear test 4096;part_4_a,,,rw,0 6144 linear test 6144,6144 4096 linear test 22528;part_2_a,,,rw,0 2048 linear test 12288;part_5_a,,,rw,0 8192 linear test 14336,8192 2048 linear test 26624";
     std::string output_list_tables = "part_1_a 0 2048 linear test 2048\npart_3_a 0 2048 linear test 4096\npart_4_a 0 6144 linear test 6144\\n6144 4096 linear test 22528\npart_2_a 0 2048 linear test 12288\npart_5_a 0 8192 linear test 14336\\n8192 2048 linear test 26624";
+    std::string output_free = "super_free,,,rw,0 4096 linear test 28672";
+    std::string output_free_list_tables = "super_free 0 4096 linear test 28672";
+
     std::ostringstream messages;
 
     EXPECT_EQ(go(metadata, false, "test", messages), output);
     EXPECT_EQ(messages.str(), "");
 
+    messages.str("");
+    messages.clear();
+
     EXPECT_EQ(go(metadata, true, "test", messages), output_list_tables);
     EXPECT_EQ(messages.str(), "");
+
+    messages.str("");
+    messages.clear();
+
+    EXPECT_EQ(go_free(metadata, false, "test", messages), output_free);
+    EXPECT_EQ(messages.str(), "");
+
+    messages.str("");
+    messages.clear();
+
+    EXPECT_EQ(go_free(metadata, true, "test", messages), output_free_list_tables);
+    EXPECT_EQ(messages.str(), "");
 }
 
 // One group with five partitions; one partition consist of three extents
 TEST(ParseDynpartsTest, GroupAFivePartsOneHasThreeExtents) {
-    LpMetadata metadata {};
-
 #ifdef METADATA_IO
     // super_partitioned_3.img based on super_4.img
     std::string metadata_str = "1634485351 52 65536 1 4096 120 75 47 222 48 213 6 78 122 230 66 214 51 226 131 122 78 100 23 86 88 86 36 222 17 7 236 187 122 127 108 9  1095520304 10 0 128 588 0 0 5 52  260 7 24  428 2 48  524 1 64  100 67 158 102 9 185 191 145 244 147 52 179 150 1 80 117 32 77 229 6 72 25 144 175 185 79 122 183 100 217 11 253 207 3 41 136 200 228 159 31 201 120 210 187 225 216 137 53 141 119 25 246 180 148 229 255 42 241 168 240 213 85 125 123 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  5 0 0 1 1 112 97 114 116 95 52 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 1 1 1 112 97 114 116 95 51 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 2 1 1 112 97 114 116 95 50 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 3 1 1 112 97 114 116 95 49 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 4 3 1 112 97 114 116 95 53 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  7 10240 0 32768 0  2048 0 22528 0  2048 0 12288 0  2048 0 2048 0  8192 0 4096 0  8192 0 14336 0  4096 0 24576 0  2 0 0 100 101 102 97 117 108 116 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 20971520 103 114 111 117 112 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  1 2048 1048576 0 22020096 0 115 117 112 101 114 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0";
+    LpMetadata metadata {};
     metadataio::readMetadata(metadata, metadata_str);
 #else
-    addPartitions(metadata, 1, 5);
-
-    strncpy(metadata.partitions[0].name, "part_4_a", 36);
-
-    strncpy(metadata.partitions[1].name, "part_3_a", 36);
-
-    strncpy(metadata.partitions[2].name, "part_2_a", 36);
-
-    strncpy(metadata.partitions[3].name, "part_1_a", 36);
-
-    metadata.partitions[4].num_extents = 3;
-
-    metadata.extents[0].num_sectors = 10240;
-    metadata.extents[0].target_data = 32768;
-
-    metadata.extents[1].num_sectors = 2048;
-    metadata.extents[1].target_data = 22528;
-
-    metadata.extents[2].num_sectors = 2048;
-    metadata.extents[2].target_data = 12288;
-
-    metadata.extents[3].num_sectors = 2048;
-    metadata.extents[3].target_data = 2048;
-
-    metadata.extents[4].num_sectors = 8192;
-    metadata.extents[4].target_data = 4096;
-
-    LpMetadataExtent e;
-    e.num_sectors = 8192;
-    e.target_type = 0;
-    e.target_data = 14336;
-    e.target_source = 0;
-
-    metadata.extents.push_back(e);
-
-    e.num_sectors = 4096;
-    e.target_type = 0;
-    e.target_data = 24576;
-    e.target_source = 0;
-
-    metadata.extents.push_back(e);
+    auto metadata_ptr = addPartitions(1, 4);
+    auto builder = android::fs_mgr::MetadataBuilder::New(*metadata_ptr);
+
+    builder->RemovePartition("part_3_a");
+    auto part = builder->AddPartition("part_3_a", "group_a", LP_PARTITION_ATTR_NONE);
+    EXPECT_NE(part, nullptr);
+    EXPECT_EQ(builder->ResizePartition(part, 1024 * 1024), true);
+
+    builder->RemovePartition("part_2_a");
+    part = builder->AddPartition("part_2_a", "group_a", LP_PARTITION_ATTR_NONE);
+    EXPECT_NE(part, nullptr);
+    EXPECT_EQ(builder->ResizePartition(part, 1024 * 1024), true);
+
+    builder->RemovePartition("part_1_a");
+    part = builder->AddPartition("part_1_a", "group_a", LP_PARTITION_ATTR_NONE);
+    EXPECT_NE(part, nullptr);
+    EXPECT_EQ(builder->ResizePartition(part, 1024 * 1024), true);
+
+    part = builder->AddPartition("part_5_a", "group_a", LP_PARTITION_ATTR_NONE);
+    EXPECT_NE(part, nullptr);
+    EXPECT_EQ(builder->ResizePartition(part, 10 * 1024 * 1024), true);
+
+    metadata_ptr = builder->Export();
+    LpMetadata &metadata = *metadata_ptr;
 #endif
 
     std::string output = "part_4_a,,,rw,0 10240 linear test 32768;part_3_a,,,rw,0 2048 linear test 22528;part_2_a,,,rw,0 2048 linear test 12288;part_1_a,,,rw,0 2048 linear test 2048;part_5_a,,,rw,0 8192 linear test 4096,8192 8192 linear test 14336,16384 4096 linear test 24576";
     std::string output_list_tables = "part_4_a 0 10240 linear test 32768\npart_3_a 0 2048 linear test 22528\npart_2_a 0 2048 linear test 12288\npart_1_a 0 2048 linear test 2048\npart_5_a 0 8192 linear test 4096\\n8192 8192 linear test 14336\\n16384 4096 linear test 24576";
+    std::string output_free = "super_free,,,rw,0 4096 linear test 28672";
+    std::string output_free_list_tables = "super_free 0 4096 linear test 28672";
+
     std::ostringstream messages;
 
     EXPECT_EQ(go(metadata, false, "test", messages), output);
     EXPECT_EQ(messages.str(), "");
 
+    messages.str("");
+    messages.clear();
+
     EXPECT_EQ(go(metadata, true, "test", messages), output_list_tables);
     EXPECT_EQ(messages.str(), "");
+
+    messages.str("");
+    messages.clear();
+
+    EXPECT_EQ(go_free(metadata, false, "test", messages), output_free);
+    EXPECT_EQ(messages.str(), "");
+
+    messages.str("");
+    messages.clear();
+
+    EXPECT_EQ(go_free(metadata, true, "test", messages), output_free_list_tables);
+    EXPECT_EQ(messages.str(), "");
 }
 
 // Two groups; First has four partitions; One partition has two extents; Second group has three partitions
 TEST(ParseDynpartsTest, GroupAFourPartsOneHasTwoExtentsGroupBThreeParts) {
-    LpMetadata metadata {};
-
 #ifdef METADATA_IO
     // super_partitioned_4.img based on super_7.img
     std::string metadata_str = "1634485351 52 65536 2 4096 78 49 207 100 39 84 66 244 14 37 199 114 161 141 31 204 216 177 41 18 50 229 147 246 94 82 47 199 172 7 223 3  1095520304 10 0 128 764 0 0 7 52  364 8 24  556 3 48  700 1 64  198 39 137 113 53 13 241 29 164 16 135 70 50 179 146 18 33 232 109 204 190 81 254 153 145 166 196 17 225 79 207 64 253 84 240 176 19 187 121 118 144 122 225 243 137 10 28 31 158 220 17 207 29 208 4 94 10 63 195 9 70 67 163 121 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  7 0 0 1 1 112 97 114 116 95 50 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 1 1 2 112 97 114 116 95 49 95 98 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 2 1 2 112 97 114 116 95 50 95 98 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 3 1 2 112 97 114 116 95 51 95 98 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 4 1 1 112 97 114 116 95 49 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 5 1 1 112 97 114 116 95 51 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 6 2 1 112 97 114 116 95 52 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  8 10240 0 12288 0  10240 0 32768 0  10240 0 43008 0  10240 0 53248 0  2048 0 2048 0  2048 0 4096 0  6144 0 6144 0  4096 0 22528 0  3 0 0 100 101 102 97 117 108 116 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 15728640 103 114 111 117 112 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 15728640 103 114 111 117 112 95 98 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  1 2048 1048576 0 32505856 0 115 117 112 101 114 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0";
+    LpMetadata metadata {};
     metadataio::readMetadata(metadata, metadata_str);
 #else
-    addPartitions(metadata, 2, 3);
-
-    strncpy(metadata.partitions[0].name, "part_2_a", 36);
-
-    strncpy(metadata.partitions[1].name, "part_1_b", 36);
-    metadata.partitions[1].group_index = 2;
-
-    strncpy(metadata.partitions[2].name, "part_2_b", 36);
-    metadata.partitions[2].group_index = 2;
-
-    strncpy(metadata.partitions[3].name, "part_3_b", 36);
+    auto metadata_ptr = addPartitions(2, 3);
+    auto builder = android::fs_mgr::MetadataBuilder::New(*metadata_ptr);
 
-    strncpy(metadata.partitions[4].name, "part_1_a", 36);
-    metadata.partitions[4].group_index = 1;
+    builder->RemovePartition("part_1_a");
+    auto part = builder->AddPartition("part_1_a", "group_a", LP_PARTITION_ATTR_NONE);
+    EXPECT_NE(part, nullptr);
+    EXPECT_EQ(builder->ResizePartition(part, 1024 * 1024), true);
 
-    strncpy(metadata.partitions[5].name, "part_3_a", 36);
-    metadata.partitions[5].group_index = 1;
+    builder->RemovePartition("part_3_a");
+    part = builder->AddPartition("part_3_a", "group_a", LP_PARTITION_ATTR_NONE);
+    EXPECT_NE(part, nullptr);
+    EXPECT_EQ(builder->ResizePartition(part, 1024 * 1024), true);
 
-    LpMetadataPartition p;
-    p.attributes = 0;
-    p.first_extent_index = 6;
-    p.num_extents = 2;
-    p.group_index = 1;
-    strncpy(p.name, "part_4_a", 36);
+    part = builder->AddPartition("part_4_a", "group_a", LP_PARTITION_ATTR_NONE);
+    EXPECT_NE(part, nullptr);
+    EXPECT_EQ(builder->ResizePartition(part, 5 * 1024 * 1024), true);
 
-    metadata.partitions.push_back(p);
-
-    metadata.extents[0].num_sectors = 10240;
-    metadata.extents[0].target_data = 12288;
-
-    metadata.extents[1].num_sectors = 10240;
-    metadata.extents[1].target_data = 32768;
-
-    metadata.extents[2].num_sectors = 10240;
-    metadata.extents[2].target_data = 43008;
-
-    metadata.extents[3].num_sectors = 10240;
-    metadata.extents[3].target_data = 53248;
-
-    metadata.extents[4].num_sectors = 2048;
-    metadata.extents[4].target_data = 2048;
-
-    metadata.extents[5].num_sectors = 2048;
-    metadata.extents[5].target_data = 4096;
-
-    LpMetadataExtent e;
-    e.num_sectors = 6144;
-    e.target_type = 0;
-    e.target_data = 6144;
-    e.target_source = 0;
-
-    metadata.extents.push_back(e);
-
-    e.num_sectors = 4096;
-    e.target_type = 0;
-    e.target_data = 22528;
-    e.target_source = 0;
-
-    metadata.extents.push_back(e);
+    metadata_ptr = builder->Export();
+    LpMetadata &metadata = *metadata_ptr;
 #endif
 
     std::string output = "part_2_a,,,rw,0 10240 linear test 12288;part_1_b,,,rw,0 10240 linear test 32768;part_2_b,,,rw,0 10240 linear test 43008;part_3_b,,,rw,0 10240 linear test 53248;part_1_a,,,rw,0 2048 linear test 2048;part_3_a,,,rw,0 2048 linear test 4096;part_4_a,,,rw,0 6144 linear test 6144,6144 4096 linear test 22528";
     std::string output_list_tables = "part_2_a 0 10240 linear test 12288\npart_1_b 0 10240 linear test 32768\npart_2_b 0 10240 linear test 43008\npart_3_b 0 10240 linear test 53248\npart_1_a 0 2048 linear test 2048\npart_3_a 0 2048 linear test 4096\npart_4_a 0 6144 linear test 6144\\n6144 4096 linear test 22528";
+    std::string output_free = "super_free,,,rw,0 6144 linear test 26624";
+    std::string output_free_list_tables = "super_free 0 6144 linear test 26624";
+
     std::ostringstream messages;
 
     EXPECT_EQ(go(metadata, false, "test", messages), output);
     EXPECT_EQ(messages.str(), "");
 
+    messages.str("");
+    messages.clear();
+
     EXPECT_EQ(go(metadata, true, "test", messages), output_list_tables);
     EXPECT_EQ(messages.str(), "");
+
+    messages.str("");
+    messages.clear();
+
+    EXPECT_EQ(go_free(metadata, false, "test", messages), output_free);
+    EXPECT_EQ(messages.str(), "");
+
+    messages.str("");
+    messages.clear();
+
+    EXPECT_EQ(go_free(metadata, true, "test", messages), output_free_list_tables);
+    EXPECT_EQ(messages.str(), "");
 }
 
 // Two groups; First has three partitions; Second group has four partitions; One partition has two extents
 TEST(ParseDynpartsTest, GroupAThreePartsGroupBFourPartsOneHasTwoExtents) {
-    LpMetadata metadata {};
-
 #ifdef METADATA_IO
     // super_partitioned_5.img based on super_7.img
     std::string metadata_str = "1634485351 52 65536 2 4096 78 49 207 100 39 84 66 244 14 37 199 114 161 141 31 204 216 177 41 18 50 229 147 246 94 82 47 199 172 7 223 3  1095520304 10 0 128 764 0 0 7 52  364 8 24  556 3 48  700 1 64  172 34 125 244 217 227 56 198 196 57 66 159 75 5 75 26 201 151 120 220 210 224 106 22 229 179 139 59 7 70 91 253 27 204 4 10 117 183 48 238 0 228 135 248 206 221 161 77 18 220 250 173 233 91 10 135 107 128 233 70 149 225 147 72 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  7 0 0 1 1 112 97 114 116 95 49 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 1 1 1 112 97 114 116 95 50 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 2 1 1 112 97 114 116 95 51 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 3 1 2 112 97 114 116 95 50 95 98 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 4 1 2 112 97 114 116 95 49 95 98 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 5 1 2 112 97 114 116 95 51 95 98 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 6 2 2 112 97 114 116 95 52 95 98 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  8 10240 0 2048 0  10240 0 12288 0  10240 0 22528 0  10240 0 43008 0  2048 0 32768 0  2048 0 34816 0  6144 0 36864 0  4096 0 53248 0  3 0 0 100 101 102 97 117 108 116 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 15728640 103 114 111 117 112 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 15728640 103 114 111 117 112 95 98 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  1 2048 1048576 0 32505856 0 115 117 112 101 114 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0";
+    LpMetadata metadata {};
     metadataio::readMetadata(metadata, metadata_str);
 #else
-    addPartitions(metadata, 2, 3);
-
-    strncpy(metadata.partitions[3].name, "part_2_b", 36);
-
-    strncpy(metadata.partitions[4].name, "part_1_b", 36);
-
-    LpMetadataPartition p;
-    p.attributes = 0;
-    p.first_extent_index = 6;
-    p.num_extents = 2;
-    p.group_index = 2;
-    strncpy(p.name, "part_4_b", 36);
-
-    metadata.partitions.push_back(p);
-
-    metadata.extents[0].num_sectors = 10240;
-    metadata.extents[0].target_data = 2048;
-
-    metadata.extents[1].num_sectors = 10240;
-    metadata.extents[1].target_data = 12288;
-
-    metadata.extents[2].num_sectors = 10240;
-    metadata.extents[2].target_data = 22528;
-
-    metadata.extents[3].num_sectors = 10240;
-    metadata.extents[3].target_data = 43008;
+    auto metadata_ptr = addPartitions(2, 3);
+    auto builder = android::fs_mgr::MetadataBuilder::New(*metadata_ptr);
 
-    metadata.extents[4].num_sectors = 2048;
-    metadata.extents[4].target_data = 32768;
+    builder->RemovePartition("part_1_b");
+    auto part = builder->AddPartition("part_1_b", "group_b", LP_PARTITION_ATTR_NONE);
+    EXPECT_NE(part, nullptr);
+    EXPECT_EQ(builder->ResizePartition(part, 1024 * 1024), true);
 
-    metadata.extents[5].num_sectors = 2048;
-    metadata.extents[5].target_data = 34816;
+    builder->RemovePartition("part_3_b");
+    part = builder->AddPartition("part_3_b", "group_b", LP_PARTITION_ATTR_NONE);
+    EXPECT_NE(part, nullptr);
+    EXPECT_EQ(builder->ResizePartition(part, 1024 * 1024), true);
 
-    LpMetadataExtent e;
-    e.num_sectors = 6144;
-    e.target_type = 0;
-    e.target_data = 36864;
-    e.target_source = 0;
+    part = builder->AddPartition("part_4_b", "group_b", LP_PARTITION_ATTR_NONE);
+    EXPECT_NE(part, nullptr);
+    EXPECT_EQ(builder->ResizePartition(part, 5 * 1024 * 1024), true);
 
-    metadata.extents.push_back(e);
-
-    e.num_sectors = 4096;
-    e.target_type = 0;
-    e.target_data = 53248;
-    e.target_source = 0;
-
-    metadata.extents.push_back(e);
+    metadata_ptr = builder->Export();
+    LpMetadata &metadata = *metadata_ptr;
 #endif
 
     std::string output = "part_1_a,,,rw,0 10240 linear test 2048;part_2_a,,,rw,0 10240 linear test 12288;part_3_a,,,rw,0 10240 linear test 22528;part_2_b,,,rw,0 10240 linear test 43008;part_1_b,,,rw,0 2048 linear test 32768;part_3_b,,,rw,0 2048 linear test 34816;part_4_b,,,rw,0 6144 linear test 36864,6144 4096 linear test 53248";
     std::string output_list_tables = "part_1_a 0 10240 linear test 2048\npart_2_a 0 10240 linear test 12288\npart_3_a 0 10240 linear test 22528\npart_2_b 0 10240 linear test 43008\npart_1_b 0 2048 linear test 32768\npart_3_b 0 2048 linear test 34816\npart_4_b 0 6144 linear test 36864\\n6144 4096 linear test 53248";
+    std::string output_free = "super_free,,,rw,0 6144 linear test 57344";
+    std::string output_free_list_tables = "super_free 0 6144 linear test 57344";
+
     std::ostringstream messages;
 
     EXPECT_EQ(go(metadata, false, "test", messages), output);
     EXPECT_EQ(messages.str(), "");
 
+    messages.str("");
+    messages.clear();
+
     EXPECT_EQ(go(metadata, true, "test", messages), output_list_tables);
     EXPECT_EQ(messages.str(), "");
+
+    messages.str("");
+    messages.clear();
+
+    EXPECT_EQ(go_free(metadata, false, "test", messages), output_free);
+    EXPECT_EQ(messages.str(), "");
+
+    messages.str("");
+    messages.clear();
+
+    EXPECT_EQ(go_free(metadata, true, "test", messages), output_free_list_tables);
+    EXPECT_EQ(messages.str(), "");
 }
 
 // Two groups; First has four partitions; One partition has two extents; Second group has four partitions
 TEST(ParseDynpartsTest, GroupAFourPartsOneHasTwoExtentsGroupBFourParts) {
-    LpMetadata metadata {};
-
 #ifdef METADATA_IO
     // super_partitioned_6.img based on super_7.img
     std::string metadata_str = "1634485351 52 65536 2 4096 78 49 207 100 39 84 66 244 14 37 199 114 161 141 31 204 216 177 41 18 50 229 147 246 94 82 47 199 172 7 223 3  1095520304 10 0 128 840 0 0 8 52  416 9 24  632 3 48  776 1 64  21 233 41 204 110 189 99 227 103 32 29 115 169 0 49 156 54 147 216 58 13 63 135 111 149 144 250 205 58 96 217 234 8 65 18 137 114 182 215 45 54 149 243 40 233 128 136 53 186 77 19 141 140 131 83 185 58 72 178 55 250 98 181 140 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  8 0 0 1 1 112 97 114 116 95 50 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 1 1 2 112 97 114 116 95 50 95 98 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 2 1 1 112 97 114 116 95 49 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 3 1 1 112 97 114 116 95 51 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 4 2 1 112 97 114 116 95 52 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 6 1 2 112 97 114 116 95 49 95 98 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 7 1 2 112 97 114 116 95 51 95 98 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 8 1 2 112 97 114 116 95 52 95 98 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  9 10240 0 12288 0  10240 0 43008 0  2048 0 2048 0  2048 0 4096 0  6144 0 6144 0  4096 0 22528 0  2048 0 26624 0  2048 0 28672 0  10240 0 30720 0  3 0 0 100 101 102 97 117 108 116 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 15728640 103 114 111 117 112 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 15728640 103 114 111 117 112 95 98 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  1 2048 1048576 0 32505856 0 115 117 112 101 114 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0";
+    LpMetadata metadata {};
     metadataio::readMetadata(metadata, metadata_str);
+    std::string output = "part_2_a,,,rw,0 10240 linear test 12288;part_2_b,,,rw,0 10240 linear test 43008;part_1_a,,,rw,0 2048 linear test 2048;part_3_a,,,rw,0 2048 linear test 4096;part_4_a,,,rw,0 6144 linear test 6144,6144 4096 linear test 22528;part_1_b,,,rw,0 2048 linear test 26624;part_3_b,,,rw,0 2048 linear test 28672;part_4_b,,,rw,0 10240 linear test 30720";
+    std::string output_list_tables = "part_2_a 0 10240 linear test 12288\npart_2_b 0 10240 linear test 43008\npart_1_a 0 2048 linear test 2048\npart_3_a 0 2048 linear test 4096\npart_4_a 0 6144 linear test 6144\\n6144 4096 linear test 22528\npart_1_b 0 2048 linear test 26624\npart_3_b 0 2048 linear test 28672\npart_4_b 0 10240 linear test 30720";
+    std::string output_free = "super_free,,,rw,0 2048 linear test 40960,2048 10240 linear test 53248";
+    std::string output_free_list_tables = "super_free 0 2048 linear test 40960\\n2048 10240 linear test 53248";
 #else
-    addPartitions(metadata, 2, 4);
-
-    strncpy(metadata.partitions[0].name, "part_2_a", 36);
-
-    strncpy(metadata.partitions[1].name, "part_2_b", 36);
-    metadata.partitions[1].group_index = 2;
-
-    strncpy(metadata.partitions[2].name, "part_1_a", 36);
-
-    strncpy(metadata.partitions[3].name, "part_3_a", 36);
-
-    strncpy(metadata.partitions[4].name, "part_4_a", 36);
-    metadata.partitions[4].group_index = 1;
-    metadata.partitions[4].num_extents = 2;
-
-    strncpy(metadata.partitions[5].name, "part_1_b", 36);
-    metadata.partitions[5].first_extent_index = 6;
-
-    metadata.partitions[6].first_extent_index = 7;
-
-    metadata.partitions[7].first_extent_index = 8;
-
-    metadata.extents[0].num_sectors = 10240;
-    metadata.extents[0].target_data = 12288;
-
-    metadata.extents[1].num_sectors = 10240;
-    metadata.extents[1].target_data = 43008;
-
-    metadata.extents[2].num_sectors = 2048;
-    metadata.extents[2].target_data = 2048;
-
-    metadata.extents[3].num_sectors = 2048;
-    metadata.extents[3].target_data = 4096;
-
-    metadata.extents[4].num_sectors = 6144;
-    metadata.extents[4].target_data = 6144;
-
-    metadata.extents[5].num_sectors = 4096;
-    metadata.extents[5].target_data = 22528;
-
-    metadata.extents[6].num_sectors = 2048;
-    metadata.extents[6].target_data = 26624;
-
-    metadata.extents[7].num_sectors = 2048;
-    metadata.extents[7].target_data = 28672;
-
-    LpMetadataExtent e;
-    e.num_sectors = 10240;
-    e.target_type = 0;
-    e.target_data = 30720;
-    e.target_source = 0;
-
-    metadata.extents.push_back(e);
+    auto metadata_ptr = addPartitions(2, 3);
+    auto builder = android::fs_mgr::MetadataBuilder::New(*metadata_ptr);
+
+    builder->RemovePartition("part_1_a");
+    auto part = builder->AddPartition("part_1_a", "group_a", LP_PARTITION_ATTR_NONE);
+    EXPECT_NE(part, nullptr);
+    EXPECT_EQ(builder->ResizePartition(part, 1024 * 1024), true);
+
+    builder->RemovePartition("part_3_a");
+    part = builder->AddPartition("part_3_a", "group_a", LP_PARTITION_ATTR_NONE);
+    EXPECT_NE(part, nullptr);
+    EXPECT_EQ(builder->ResizePartition(part, 1024 * 1024), true);
+
+    part = builder->AddPartition("part_4_a", "group_a", LP_PARTITION_ATTR_NONE);
+    EXPECT_NE(part, nullptr);
+    EXPECT_EQ(builder->ResizePartition(part, 5 * 1024 * 1024), true);
+
+    builder->RemovePartition("part_1_b");
+    part = builder->AddPartition("part_1_b", "group_b", LP_PARTITION_ATTR_NONE);
+    EXPECT_NE(part, nullptr);
+    EXPECT_EQ(builder->ResizePartition(part, 1024 * 1024), true);
+
+    builder->RemovePartition("part_3_b");
+    part = builder->AddPartition("part_3_b", "group_b", LP_PARTITION_ATTR_NONE);
+    EXPECT_NE(part, nullptr);
+    EXPECT_EQ(builder->ResizePartition(part, 1024 * 1024), true);
+
+    part = builder->AddPartition("part_4_b", "group_b", LP_PARTITION_ATTR_NONE);
+    EXPECT_NE(part, nullptr);
+    EXPECT_EQ(builder->ResizePartition(part, 5 * 1024 * 1024), true);
+
+    metadata_ptr = builder->Export();
+    LpMetadata &metadata = *metadata_ptr;
+
+    std::string output = "part_2_a,,,rw,0 10240 linear test 12288;part_2_b,,,rw,0 10240 linear test 43008;part_1_a,,,rw,0 2048 linear test 2048;part_3_a,,,rw,0 2048 linear test 4096;part_4_a,,,rw,0 6144 linear test 6144,6144 4096 linear test 22528;part_1_b,,,rw,0 2048 linear test 32768;part_3_b,,,rw,0 2048 linear test 34816;part_4_b,,,rw,0 6144 linear test 36864,6144 4096 linear test 53248";
+    std::string output_list_tables = "part_2_a 0 10240 linear test 12288\npart_2_b 0 10240 linear test 43008\npart_1_a 0 2048 linear test 2048\npart_3_a 0 2048 linear test 4096\npart_4_a 0 6144 linear test 6144\\n6144 4096 linear test 22528\npart_1_b 0 2048 linear test 32768\npart_3_b 0 2048 linear test 34816\npart_4_b 0 6144 linear test 36864\\n6144 4096 linear test 53248";
+    std::string output_free = "super_free,,,rw,0 6144 linear test 26624,6144 6144 linear test 57344";
+    std::string output_free_list_tables = "super_free 0 6144 linear test 26624\\n6144 6144 linear test 57344";
 #endif
 
-    std::string output = "part_2_a,,,rw,0 10240 linear test 12288;part_2_b,,,rw,0 10240 linear test 43008;part_1_a,,,rw,0 2048 linear test 2048;part_3_a,,,rw,0 2048 linear test 4096;part_4_a,,,rw,0 6144 linear test 6144,6144 4096 linear test 22528;part_1_b,,,rw,0 2048 linear test 26624;part_3_b,,,rw,0 2048 linear test 28672;part_4_b,,,rw,0 10240 linear test 30720";
-    std::string output_list_tables = "part_2_a 0 10240 linear test 12288\npart_2_b 0 10240 linear test 43008\npart_1_a 0 2048 linear test 2048\npart_3_a 0 2048 linear test 4096\npart_4_a 0 6144 linear test 6144\\n6144 4096 linear test 22528\npart_1_b 0 2048 linear test 26624\npart_3_b 0 2048 linear test 28672\npart_4_b 0 10240 linear test 30720";
     std::ostringstream messages;
 
     EXPECT_EQ(go(metadata, false, "test", messages), output);
     EXPECT_EQ(messages.str(), "");
 
+    messages.str("");
+    messages.clear();
+
     EXPECT_EQ(go(metadata, true, "test", messages), output_list_tables);
     EXPECT_EQ(messages.str(), "");
+
+    messages.str("");
+    messages.clear();
+
+    EXPECT_EQ(go_free(metadata, false, "test", messages), output_free);
+    EXPECT_EQ(messages.str(), "");
+
+    messages.str("");
+    messages.clear();
+
+    EXPECT_EQ(go_free(metadata, true, "test", messages), output_free_list_tables);
+    EXPECT_EQ(messages.str(), "");
 }
 
 // Two groups; First has five partitions; One partition has three extents; Second group has four partitions
 TEST(ParseDynpartsTest, GroupAFivePartsOneHasThreeExtentsGroupBFourParts) {
-    LpMetadata metadata {};
-
 #ifdef METADATA_IO
     // super_partitioned_7.img based on super_8.img
     std::string metadata_str = "1634485351 52 65536 2 4096 78 49 207 100 39 84 66 244 14 37 199 114 161 141 31 204 216 177 41 18 50 229 147 246 94 82 47 199 172 7 223 3  1095520304 10 0 128 940 0 0 9 52  468 11 24  732 3 48  876 1 64  38 157 64 86 69 136 89 208 53 119 189 46 238 250 109 107 35 163 2 98 153 127 97 131 38 164 29 163 223 90 60 45 156 188 72 154 94 33 123 149 8 138 35 29 238 233 46 251 215 121 182 44 187 60 141 14 210 154 102 61 210 233 201 246 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  9 0 0 1 1 112 97 114 116 95 52 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 1 1 2 112 97 114 116 95 49 95 98 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 2 1 2 112 97 114 116 95 50 95 98 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 3 1 2 112 97 114 116 95 51 95 98 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 4 1 2 112 97 114 116 95 52 95 98 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 5 1 1 112 97 114 116 95 51 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 6 1 1 112 97 114 116 95 50 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 7 1 1 112 97 114 116 95 49 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 8 3 1 112 97 114 116 95 53 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  11 10240 0 32768 0  10240 0 43008 0  10240 0 53248 0  10240 0 63488 0  10240 0 73728 0  2048 0 22528 0  2048 0 12288 0  2048 0 2048 0  8192 0 4096 0  8192 0 14336 0  4096 0 24576 0  3 0 0 100 101 102 97 117 108 116 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 20971520 103 114 111 117 112 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 20971520 103 114 111 117 112 95 98 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  1 2048 1048576 0 42991616 0 115 117 112 101 114 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0";
+    LpMetadata metadata {};
     metadataio::readMetadata(metadata, metadata_str);
 #else
-    addPartitions(metadata, 2, 4);
-
-    strncpy(metadata.partitions[0].name, "part_4_a", 36);
-
-    strncpy(metadata.partitions[1].name, "part_1_b", 36);
-    metadata.partitions[1].group_index = 2;
-
-    strncpy(metadata.partitions[2].name, "part_2_b", 36);
-    metadata.partitions[2].group_index = 2;
-
-    strncpy(metadata.partitions[3].name, "part_3_b", 36);
-    metadata.partitions[3].group_index = 2;
-
-    strncpy(metadata.partitions[4].name, "part_4_b", 36);
-
-    strncpy(metadata.partitions[5].name, "part_3_a", 36);
-    metadata.partitions[5].group_index = 1;
-
-    strncpy(metadata.partitions[6].name, "part_2_a", 36);
-    metadata.partitions[6].group_index = 1;
-
-    strncpy(metadata.partitions[7].name, "part_1_a", 36);
-    metadata.partitions[7].group_index = 1;
-
-    LpMetadataPartition p;
-    p.attributes = 0;
-    p.first_extent_index = 8;
-    p.num_extents = 3;
-    p.group_index = 1;
-    strncpy(p.name, "part_5_a", 36);
-
-    metadata.partitions.push_back(p);
-
-    metadata.extents[0].num_sectors = 10240;
-    metadata.extents[0].target_data = 32768;
-
-    metadata.extents[1].num_sectors = 10240;
-    metadata.extents[1].target_data = 43008;
-
-    metadata.extents[2].num_sectors = 10240;
-    metadata.extents[2].target_data = 53248;
-
-    metadata.extents[3].num_sectors = 10240;
-    metadata.extents[3].target_data = 63488;
-
-    metadata.extents[4].num_sectors = 10240;
-    metadata.extents[4].target_data = 73728;
-
-    metadata.extents[5].num_sectors = 2048;
-    metadata.extents[5].target_data = 22528;
-
-    metadata.extents[6].num_sectors = 2048;
-    metadata.extents[6].target_data = 12288;
-
-    metadata.extents[7].num_sectors = 2048;
-    metadata.extents[7].target_data = 2048;
-
-    LpMetadataExtent e;
-    e.num_sectors = 8192;
-    e.target_type = 0;
-    e.target_data = 4096;
-    e.target_source = 0;
-
-    metadata.extents.push_back(e);
-
-    e.num_sectors = 8192;
-    e.target_type = 0;
-    e.target_data = 14336;
-    e.target_source = 0;
-
-    metadata.extents.push_back(e);
-
-    e.num_sectors = 4096;
-    e.target_type = 0;
-    e.target_data = 24576;
-    e.target_source = 0;
-
-    metadata.extents.push_back(e);
+    auto metadata_ptr = addPartitions(2, 4);
+    auto builder = android::fs_mgr::MetadataBuilder::New(*metadata_ptr);
+
+    builder->RemovePartition("part_3_a");
+    auto part = builder->AddPartition("part_3_a", "group_a", LP_PARTITION_ATTR_NONE);
+    EXPECT_NE(part, nullptr);
+    EXPECT_EQ(builder->ResizePartition(part, 1024 * 1024), true);
+
+    builder->RemovePartition("part_2_a");
+    part = builder->AddPartition("part_2_a", "group_a", LP_PARTITION_ATTR_NONE);
+    EXPECT_NE(part, nullptr);
+    EXPECT_EQ(builder->ResizePartition(part, 1024 * 1024), true);
+
+    builder->RemovePartition("part_1_a");
+    part = builder->AddPartition("part_1_a", "group_a", LP_PARTITION_ATTR_NONE);
+    EXPECT_NE(part, nullptr);
+    EXPECT_EQ(builder->ResizePartition(part, 1024 * 1024), true);
+
+    part = builder->AddPartition("part_5_a", "group_a", LP_PARTITION_ATTR_NONE);
+    EXPECT_NE(part, nullptr);
+    EXPECT_EQ(builder->ResizePartition(part, 10 * 1024 * 1024), true);
+
+    metadata_ptr = builder->Export();
+    LpMetadata &metadata = *metadata_ptr;
 #endif
 
     std::string output = "part_4_a,,,rw,0 10240 linear test 32768;part_1_b,,,rw,0 10240 linear test 43008;part_2_b,,,rw,0 10240 linear test 53248;part_3_b,,,rw,0 10240 linear test 63488;part_4_b,,,rw,0 10240 linear test 73728;part_3_a,,,rw,0 2048 linear test 22528;part_2_a,,,rw,0 2048 linear test 12288;part_1_a,,,rw,0 2048 linear test 2048;part_5_a,,,rw,0 8192 linear test 4096,8192 8192 linear test 14336,16384 4096 linear test 24576";
     std::string output_list_tables = "part_4_a 0 10240 linear test 32768\npart_1_b 0 10240 linear test 43008\npart_2_b 0 10240 linear test 53248\npart_3_b 0 10240 linear test 63488\npart_4_b 0 10240 linear test 73728\npart_3_a 0 2048 linear test 22528\npart_2_a 0 2048 linear test 12288\npart_1_a 0 2048 linear test 2048\npart_5_a 0 8192 linear test 4096\\n8192 8192 linear test 14336\\n16384 4096 linear test 24576";
+    std::string output_free = "super_free,,,rw,0 4096 linear test 28672";
+    std::string output_free_list_tables = "super_free 0 4096 linear test 28672";
+
     std::ostringstream messages;
 
     EXPECT_EQ(go(metadata, false, "test", messages), output);
     EXPECT_EQ(messages.str(), "");
 
+    messages.str("");
+    messages.clear();
+
     EXPECT_EQ(go(metadata, true, "test", messages), output_list_tables);
     EXPECT_EQ(messages.str(), "");
+
+    messages.str("");
+    messages.clear();
+
+    EXPECT_EQ(go_free(metadata, false, "test", messages), output_free);
+    EXPECT_EQ(messages.str(), "");
+
+    messages.str("");
+    messages.clear();
+
+    EXPECT_EQ(go_free(metadata, true, "test", messages), output_free_list_tables);
+    EXPECT_EQ(messages.str(), "");
 }
 
 // Two groups; First has four partitions; Second group has four partitions; One partition has three extents
 TEST(ParseDynpartsTest, GroupAFourPartsGroupBFivePartsOneHasThreeExtents) {
-    LpMetadata metadata {};
-
 #ifdef METADATA_IO
     // super_partitioned_8.img based on super_8.img
     std::string metadata_str = "1634485351 52 65536 2 4096 78 49 207 100 39 84 66 244 14 37 199 114 161 141 31 204 216 177 41 18 50 229 147 246 94 82 47 199 172 7 223 3  1095520304 10 0 128 940 0 0 9 52  468 11 24  732 3 48  876 1 64  161 105 225 29 136 71 245 247 39 14 156 88 151 28 82 244 132 157 203 210 58 12 250 64 255 45 144 234 197 39 115 71 123 237 16 48 250 122 185 180 49 21 168 129 255 38 210 56 194 205 145 223 244 249 115 149 140 205 16 123 172 10 238 19 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  9 0 0 1 1 112 97 114 116 95 49 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 1 1 1 112 97 114 116 95 50 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 2 1 1 112 97 114 116 95 51 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 3 1 1 112 97 114 116 95 52 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 4 1 2 112 97 114 116 95 52 95 98 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 5 1 2 112 97 114 116 95 51 95 98 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 6 1 2 112 97 114 116 95 50 95 98 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 7 1 2 112 97 114 116 95 49 95 98 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 8 3 2 112 97 114 116 95 53 95 98 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  11 10240 0 2048 0  10240 0 12288 0  10240 0 22528 0  10240 0 32768 0  10240 0 73728 0  2048 0 63488 0  2048 0 53248 0  2048 0 43008 0  8192 0 45056 0  8192 0 55296 0  4096 0 65536 0  3 0 0 100 101 102 97 117 108 116 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 20971520 103 114 111 117 112 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 20971520 103 114 111 117 112 95 98 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  1 2048 1048576 0 42991616 0 115 117 112 101 114 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0";
+    LpMetadata metadata {};
     metadataio::readMetadata(metadata, metadata_str);
 #else
-    addPartitions(metadata, 2, 4);
-
-    strncpy(metadata.partitions[4].name, "part_4_b", 36);
-
-    strncpy(metadata.partitions[5].name, "part_3_b", 36);
-
-    strncpy(metadata.partitions[6].name, "part_2_b", 36);
-
-    strncpy(metadata.partitions[7].name, "part_1_b", 36);
-
-    LpMetadataPartition p;
-    p.attributes = 0;
-    p.first_extent_index = 8;
-    p.num_extents = 3;
-    p.group_index = 2;
-    strncpy(p.name, "part_5_b", 36);
-
-    metadata.partitions.push_back(p);
-
-    metadata.extents[0].num_sectors = 10240;
-    metadata.extents[0].target_data = 2048;
-
-    metadata.extents[1].num_sectors = 10240;
-    metadata.extents[1].target_data = 12288;
-
-    metadata.extents[2].num_sectors = 10240;
-    metadata.extents[2].target_data = 22528;
-
-    metadata.extents[3].num_sectors = 10240;
-    metadata.extents[3].target_data = 32768;
-
-    metadata.extents[4].num_sectors = 10240;
-    metadata.extents[4].target_data = 73728;
-
-    metadata.extents[5].num_sectors = 2048;
-    metadata.extents[5].target_data = 63488;
-
-    metadata.extents[6].num_sectors = 2048;
-    metadata.extents[6].target_data = 53248;
-
-    metadata.extents[7].num_sectors = 2048;
-    metadata.extents[7].target_data = 43008;
-
-    LpMetadataExtent e;
-    e.num_sectors = 8192;
-    e.target_type = 0;
-    e.target_data = 45056;
-    e.target_source = 0;
-
-    metadata.extents.push_back(e);
-
-    e.num_sectors = 8192;
-    e.target_type = 0;
-    e.target_data = 55296;
-    e.target_source = 0;
-
-    metadata.extents.push_back(e);
-
-    e.num_sectors = 4096;
-    e.target_type = 0;
-    e.target_data = 65536;
-    e.target_source = 0;
-
-    metadata.extents.push_back(e);
+    auto metadata_ptr = addPartitions(2, 4);
+    auto builder = android::fs_mgr::MetadataBuilder::New(*metadata_ptr);
+
+    builder->RemovePartition("part_3_b");
+    auto part = builder->AddPartition("part_3_b", "group_b", LP_PARTITION_ATTR_NONE);
+    EXPECT_NE(part, nullptr);
+    EXPECT_EQ(builder->ResizePartition(part, 1024 * 1024), true);
+
+    builder->RemovePartition("part_2_b");
+    part = builder->AddPartition("part_2_b", "group_b", LP_PARTITION_ATTR_NONE);
+    EXPECT_NE(part, nullptr);
+    EXPECT_EQ(builder->ResizePartition(part, 1024 * 1024), true);
+
+    builder->RemovePartition("part_1_b");
+    part = builder->AddPartition("part_1_b", "group_b", LP_PARTITION_ATTR_NONE);
+    EXPECT_NE(part, nullptr);
+    EXPECT_EQ(builder->ResizePartition(part, 1024 * 1024), true);
+
+    part = builder->AddPartition("part_5_b", "group_b", LP_PARTITION_ATTR_NONE);
+    EXPECT_NE(part, nullptr);
+    EXPECT_EQ(builder->ResizePartition(part, 10 * 1024 * 1024), true);
+
+    metadata_ptr = builder->Export();
+    LpMetadata &metadata = *metadata_ptr;
 #endif
 
     std::string output = "part_1_a,,,rw,0 10240 linear test 2048;part_2_a,,,rw,0 10240 linear test 12288;part_3_a,,,rw,0 10240 linear test 22528;part_4_a,,,rw,0 10240 linear test 32768;part_4_b,,,rw,0 10240 linear test 73728;part_3_b,,,rw,0 2048 linear test 63488;part_2_b,,,rw,0 2048 linear test 53248;part_1_b,,,rw,0 2048 linear test 43008;part_5_b,,,rw,0 8192 linear test 45056,8192 8192 linear test 55296,16384 4096 linear test 65536";
     std::string output_list_tables = "part_1_a 0 10240 linear test 2048\npart_2_a 0 10240 linear test 12288\npart_3_a 0 10240 linear test 22528\npart_4_a 0 10240 linear test 32768\npart_4_b 0 10240 linear test 73728\npart_3_b 0 2048 linear test 63488\npart_2_b 0 2048 linear test 53248\npart_1_b 0 2048 linear test 43008\npart_5_b 0 8192 linear test 45056\\n8192 8192 linear test 55296\\n16384 4096 linear test 65536";
+    std::string output_free = "super_free,,,rw,0 4096 linear test 69632";
+    std::string output_free_list_tables = "super_free 0 4096 linear test 69632";
+
     std::ostringstream messages;
 
     EXPECT_EQ(go(metadata, false, "test", messages), output);
     EXPECT_EQ(messages.str(), "");
 
+    messages.str("");
+    messages.clear();
+
     EXPECT_EQ(go(metadata, true, "test", messages), output_list_tables);
     EXPECT_EQ(messages.str(), "");
+
+    messages.str("");
+    messages.clear();
+
+    EXPECT_EQ(go_free(metadata, false, "test", messages), output_free);
+    EXPECT_EQ(messages.str(), "");
+
+    messages.str("");
+    messages.clear();
+
+    EXPECT_EQ(go_free(metadata, true, "test", messages), output_free_list_tables);
+    EXPECT_EQ(messages.str(), "");
 }
 
 // Two groups; First has five partitions; One partition has three extents; Second group has five partitions
 TEST(ParseDynpartsTest, GroupAFivePartsOneHasThreeExtentsGroupBFiveParts) {
-    LpMetadata metadata {};
-
 #ifdef METADATA_IO
     // super_partitioned_9.img based on super_8.img
     std::string metadata_str = "1634485351 52 65536 2 4096 78 49 207 100 39 84 66 244 14 37 199 114 161 141 31 204 216 177 41 18 50 229 147 246 94 82 47 199 172 7 223 3  1095520304 10 0 128 1016 0 0 10 52  520 12 24  808 3 48  952 1 64  21 53 9 199 89 140 129 245 91 161 143 41 255 149 151 157 155 203 217 8 132 22 238 245 22 56 224 117 118 185 42 167 194 50 247 62 63 4 83 80 202 200 136 62 41 177 101 250 248 251 229 180 136 119 221 159 217 173 146 147 254 84 38 251 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  10 0 0 1 1 112 97 114 116 95 52 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 1 1 2 112 97 114 116 95 52 95 98 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 2 1 1 112 97 114 116 95 51 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 3 1 1 112 97 114 116 95 50 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 4 1 1 112 97 114 116 95 49 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 5 3 1 112 97 114 116 95 53 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 8 1 2 112 97 114 116 95 51 95 98 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 9 1 2 112 97 114 116 95 50 95 98 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 10 1 2 112 97 114 116 95 49 95 98 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 11 1 2 112 97 114 116 95 53 95 98 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  12 10240 0 32768 0  10240 0 73728 0  2048 0 22528 0  2048 0 12288 0  2048 0 2048 0  8192 0 4096 0  8192 0 14336 0  4096 0 24576 0  2048 0 28672 0  2048 0 30720 0  2048 0 43008 0  20480 0 45056 0  3 0 0 100 101 102 97 117 108 116 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 20971520 103 114 111 117 112 95 97 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 20971520 103 114 111 117 112 95 98 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  1 2048 1048576 0 42991616 0 115 117 112 101 114 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0";
+    LpMetadata metadata {};
     metadataio::readMetadata(metadata, metadata_str);
-#else
-    addPartitions(metadata, 2, 5);
-
-    strncpy(metadata.partitions[0].name, "part_4_a", 36);
-
-    strncpy(metadata.partitions[1].name, "part_4_b", 36);
-    metadata.partitions[1].group_index = 2;
-
-    strncpy(metadata.partitions[3].name, "part_2_a", 36);
-
-    strncpy(metadata.partitions[4].name, "part_1_a", 36);
-
-    strncpy(metadata.partitions[5].name, "part_5_a", 36);
-    metadata.partitions[5].group_index = 1;
-    metadata.partitions[5].num_extents = 3;
-
-    strncpy(metadata.partitions[6].name, "part_3_b", 36);
-    metadata.partitions[6].first_extent_index = 8;
-
-    strncpy(metadata.partitions[7].name, "part_2_b", 36);
-    metadata.partitions[7].first_extent_index = 9;
-
-    strncpy(metadata.partitions[8].name, "part_1_b", 36);
-    metadata.partitions[8].first_extent_index = 10;
-
-    metadata.partitions[9].first_extent_index = 11;
-
-    metadata.extents[0].num_sectors = 10240;
-    metadata.extents[0].target_data = 32768;
-
-    metadata.extents[1].num_sectors = 10240;
-    metadata.extents[1].target_data = 73728;
-
-    metadata.extents[2].num_sectors = 2048;
-    metadata.extents[2].target_data = 22528;
-
-    metadata.extents[3].num_sectors = 2048;
-    metadata.extents[3].target_data = 12288;
-
-    metadata.extents[4].num_sectors = 2048;
-    metadata.extents[4].target_data = 2048;
-
-    metadata.extents[5].num_sectors = 8192;
-    metadata.extents[5].target_data = 4096;
-
-    metadata.extents[6].num_sectors = 8192;
-    metadata.extents[6].target_data = 14336;
-
-    metadata.extents[7].num_sectors = 4096;
-    metadata.extents[7].target_data = 24576;
-
-    metadata.extents[8].num_sectors = 2048;
-    metadata.extents[8].target_data = 28672;
-
-    metadata.extents[9].num_sectors = 2048;
-    metadata.extents[9].target_data = 30720;
-
-    LpMetadataExtent e;
-    e.num_sectors = 2048;
-    e.target_type = 0;
-    e.target_data = 43008;
-    e.target_source = 0;
-
-    metadata.extents.push_back(e);
-
-    e.num_sectors = 20480;
-    e.target_type = 0;
-    e.target_data = 45056;
-    e.target_source = 0;
-
-    metadata.extents.push_back(e);
-#endif
 
     std::string output = "part_4_a,,,rw,0 10240 linear test 32768;part_4_b,,,rw,0 10240 linear test 73728;part_3_a,,,rw,0 2048 linear test 22528;part_2_a,,,rw,0 2048 linear test 12288;part_1_a,,,rw,0 2048 linear test 2048;part_5_a,,,rw,0 8192 linear test 4096,8192 8192 linear test 14336,16384 4096 linear test 24576;part_3_b,,,rw,0 2048 linear test 28672;part_2_b,,,rw,0 2048 linear test 30720;part_1_b,,,rw,0 2048 linear test 43008;part_5_b,,,rw,0 20480 linear test 45056";
     std::string output_list_tables = "part_4_a 0 10240 linear test 32768\npart_4_b 0 10240 linear test 73728\npart_3_a 0 2048 linear test 22528\npart_2_a 0 2048 linear test 12288\npart_1_a 0 2048 linear test 2048\npart_5_a 0 8192 linear test 4096\\n8192 8192 linear test 14336\\n16384 4096 linear test 24576\npart_3_b 0 2048 linear test 28672\npart_2_b 0 2048 linear test 30720\npart_1_b 0 2048 linear test 43008\npart_5_b 0 20480 linear test 45056";
+    std::string output_free = "super_free,,,rw,0 8192 linear test 65536";
+    std::string output_free_list_tables = "super_free 0 8192 linear test 65536";
+#else
+    auto metadata_ptr = addPartitions(2, 4);
+    auto builder = android::fs_mgr::MetadataBuilder::New(*metadata_ptr);
+
+    builder->RemovePartition("part_3_a");
+    auto part = builder->AddPartition("part_3_a", "group_a", LP_PARTITION_ATTR_NONE);
+    EXPECT_NE(part, nullptr);
+    EXPECT_EQ(builder->ResizePartition(part, 1024 * 1024), true);
+
+    builder->RemovePartition("part_2_a");
+    part = builder->AddPartition("part_2_a", "group_a", LP_PARTITION_ATTR_NONE);
+    EXPECT_NE(part, nullptr);
+    EXPECT_EQ(builder->ResizePartition(part, 1024 * 1024), true);
+
+    builder->RemovePartition("part_1_a");
+    part = builder->AddPartition("part_1_a", "group_a", LP_PARTITION_ATTR_NONE);
+    EXPECT_NE(part, nullptr);
+    EXPECT_EQ(builder->ResizePartition(part, 1024 * 1024), true);
+
+    part = builder->AddPartition("part_5_a", "group_a", LP_PARTITION_ATTR_NONE);
+    EXPECT_NE(part, nullptr);
+    EXPECT_EQ(builder->ResizePartition(part, 10 * 1024 * 1024), true);
+
+    builder->RemovePartition("part_3_b");
+    part = builder->AddPartition("part_3_b", "group_b", LP_PARTITION_ATTR_NONE);
+    EXPECT_NE(part, nullptr);
+    EXPECT_EQ(builder->ResizePartition(part, 1024 * 1024), true);
+
+    builder->RemovePartition("part_2_b");
+    part = builder->AddPartition("part_2_b", "group_b", LP_PARTITION_ATTR_NONE);
+    EXPECT_NE(part, nullptr);
+    EXPECT_EQ(builder->ResizePartition(part, 1024 * 1024), true);
+
+    builder->RemovePartition("part_1_b");
+    part = builder->AddPartition("part_1_b", "group_b", LP_PARTITION_ATTR_NONE);
+    EXPECT_NE(part, nullptr);
+    EXPECT_EQ(builder->ResizePartition(part, 1024 * 1024), true);
+
+    part = builder->AddPartition("part_5_b", "group_b", LP_PARTITION_ATTR_NONE);
+    EXPECT_NE(part, nullptr);
+    EXPECT_EQ(builder->ResizePartition(part, 10 * 1024 * 1024), true);
+
+    metadata_ptr = builder->Export();
+    LpMetadata &metadata = *metadata_ptr;
+
+    std::string output = "part_4_a,,,rw,0 10240 linear test 32768;part_4_b,,,rw,0 10240 linear test 73728;part_3_a,,,rw,0 2048 linear test 22528;part_2_a,,,rw,0 2048 linear test 12288;part_1_a,,,rw,0 2048 linear test 2048;part_5_a,,,rw,0 8192 linear test 4096,8192 8192 linear test 14336,16384 4096 linear test 24576;part_3_b,,,rw,0 2048 linear test 63488;part_2_b,,,rw,0 2048 linear test 53248;part_1_b,,,rw,0 2048 linear test 43008;part_5_b,,,rw,0 8192 linear test 45056,8192 8192 linear test 55296,16384 4096 linear test 65536";
+    std::string output_list_tables = "part_4_a 0 10240 linear test 32768\npart_4_b 0 10240 linear test 73728\npart_3_a 0 2048 linear test 22528\npart_2_a 0 2048 linear test 12288\npart_1_a 0 2048 linear test 2048\npart_5_a 0 8192 linear test 4096\\n8192 8192 linear test 14336\\n16384 4096 linear test 24576\npart_3_b 0 2048 linear test 63488\npart_2_b 0 2048 linear test 53248\npart_1_b 0 2048 linear test 43008\npart_5_b 0 8192 linear test 45056\\n8192 8192 linear test 55296\\n16384 4096 linear test 65536";
+    std::string output_free = "super_free,,,rw,0 4096 linear test 28672,4096 4096 linear test 69632";
+    std::string output_free_list_tables = "super_free 0 4096 linear test 28672\\n4096 4096 linear test 69632";
+#endif
+
     std::ostringstream messages;
 
     EXPECT_EQ(go(metadata, false, "test", messages), output);
     EXPECT_EQ(messages.str(), "");
 
+    messages.str("");
+    messages.clear();
+
     EXPECT_EQ(go(metadata, true, "test", messages), output_list_tables);
     EXPECT_EQ(messages.str(), "");
-}
\ No newline at end of file
+
+    messages.str("");
+    messages.clear();
+
+    EXPECT_EQ(go_free(metadata, false, "test", messages), output_free);
+    EXPECT_EQ(messages.str(), "");
+
+    messages.str("");
+    messages.clear();
+
+    EXPECT_EQ(go_free(metadata, true, "test", messages), output_free_list_tables);
+    EXPECT_EQ(messages.str(), "");
+}
index 8db42bd..836248e 100755 (executable)
@@ -1,19 +1,12 @@
- #!/bin/bash
+#!/bin/bash
 
 MIB=1048576 # 1 MiB is this amount of bytes
-SUPER_ALIGNMENT=$MIB
-
-# calculate aligned sized of partitions to be placed on super
-get_aligned_size () {
-    func_result=$(($SUPER_ALIGNMENT*(($1+$SUPER_ALIGNMENT-1)/$SUPER_ALIGNMENT)))
-    echo "$func_result"
-}
 
 # super with one slot, one partition
 create_super_1 () {
     metadata_slots=1
     metadata_size=65536
-    metadata_aligned_size="$(get_aligned_size $metadata_size)" #should be 1 MB
+    metadata_aligned_size=$MIB
 
     super_size_mb=6 # MB
     super_size="$(($super_size_mb*$MIB))"
@@ -32,7 +25,7 @@ create_super_1 () {
 create_super_2 () {
     metadata_slots=1
     metadata_size=65536
-    metadata_aligned_size="$(get_aligned_size $metadata_size)" #should be 1 MB
+    metadata_aligned_size=$MIB
 
     super_size_mb=11 # MB
     super_size="$(($super_size_mb*$MIB))"
@@ -53,7 +46,7 @@ create_super_2 () {
 create_super_3 () {
     metadata_slots=1
     metadata_size=65536
-    metadata_aligned_size="$(get_aligned_size $metadata_size)" #should be 1 MB
+    metadata_aligned_size=$MIB
 
     super_size_mb=16 # MB
     super_size="$(($super_size_mb*$MIB))"
@@ -74,7 +67,7 @@ create_super_3 () {
 create_super_4 () {
     metadata_slots=1
     metadata_size=65536
-    metadata_aligned_size="$(get_aligned_size $metadata_size)" #should be 1 MB
+    metadata_aligned_size=$MIB
 
     super_size_mb=21 # MB
     super_size="$(($super_size_mb*$MIB))"
@@ -96,7 +89,7 @@ create_super_4 () {
 create_super_5 () {
     metadata_slots=2
     metadata_size=65536
-    metadata_aligned_size="$(get_aligned_size $metadata_size)" #should be 1 MB
+    metadata_aligned_size=$MIB
 
     super_size_mb=11 # MB
     super_size="$(($super_size_mb*$MIB))"
@@ -117,7 +110,7 @@ create_super_5 () {
 create_super_6 () {
     metadata_slots=2
     metadata_size=65536
-    metadata_aligned_size="$(get_aligned_size $metadata_size)" #should be 1 MB
+    metadata_aligned_size=$MIB
 
     super_size_mb=21 # MB
     super_size="$(($super_size_mb*$MIB))"
@@ -140,7 +133,7 @@ create_super_6 () {
 create_super_7 () {
     metadata_slots=2
     metadata_size=65536
-    metadata_aligned_size="$(get_aligned_size $metadata_size)" #should be 1 MB
+    metadata_aligned_size=$MIB
 
     super_size_mb=31 # MB
     super_size="$(($super_size_mb*$MIB))"
@@ -165,7 +158,7 @@ create_super_7 () {
 create_super_8 () {
     metadata_slots=2
     metadata_size=65536
-    metadata_aligned_size="$(get_aligned_size $metadata_size)" #should be 1 MB
+    metadata_aligned_size=$MIB
 
     super_size_mb=41 # MB
     super_size="$(($super_size_mb*$MIB))"
@@ -268,7 +261,6 @@ lpadd super_partitioned_8.img part_2_b group_b part_one_mib.img --replace
 lpadd super_partitioned_8.img part_1_b group_b part_one_mib.img --replace
 lpadd super_partitioned_8.img part_5_b group_b part_ten_mib.img
 
-
 cp super_8.img super_partitioned_9.img
 
 lpadd super_partitioned_9.img part_3_a group_a part_one_mib.img --replace
index 66e365c..58c7ac9 100644 (file)
@@ -6,6 +6,7 @@
 #include <string>
 
 #include <liblp/liblp.h>
+#include <liblp/builder.h>
 
 #include "metadataio.h"
 
@@ -41,6 +42,30 @@ void addPartitions(android::fs_mgr::LpMetadata &metadata, int number_of_groups,
     }
 }
 
+std::unique_ptr<android::fs_mgr::LpMetadata> addPartitions(int number_of_groups, int number_of_parts) {
+    uint64_t group_size = number_of_parts * PART_SIZE_B;
+    uint64_t super_size = ALIGNMENT + number_of_groups * group_size;
+
+    auto builder = android::fs_mgr::MetadataBuilder::New(super_size, METADATA_SIZE, number_of_groups, ALIGNMENT);
+    android::fs_mgr::Partition* part;
+    std::string group_name = "group_x";
+    const std::string group_suffix = "_x";
+
+    for(int group_idx = 0; group_idx < number_of_groups; ++group_idx) {
+        group_name[group_name.size() - 1] = 'a' + group_idx;
+        builder->AddGroup(group_name, group_size);
+
+        for(int part_idx = 0; part_idx < number_of_parts; ++part_idx) {
+            std::string part_name = "part_" + std::to_string(part_idx + 1) + group_suffix;
+            part_name[part_name.size() - 1] = 'a' + group_idx;
+            part = builder->AddPartition(part_name, group_name, LP_PARTITION_ATTR_NONE);
+            builder->ResizePartition(part, PART_SIZE_B);
+        }
+    }
+
+    return builder->Export();
+}
+
 bool metadataio::operator==(const LpMetadataBlockDevice &a, const LpMetadataBlockDevice &b) {
     if (a.first_logical_sector != b.first_logical_sector) return false;
     if (a.alignment != b.alignment) return false;
index 146f8d2..4833214 100644 (file)
@@ -4,9 +4,13 @@
 #ifndef _METADATAIO_H_
 #define _METADATAIO_H_
 
-#define PART_SIZE 5 // MB
+#define METADATA_SIZE 65536 // in bytes
+#define ALIGNMENT 1048576 // in bytes
+#define PART_SIZE 5 // in MiB
+#define PART_SIZE_B (PART_SIZE * 1024 * 1024) // in bytes
 
 void addPartitions(android::fs_mgr::LpMetadata &metadata, int number_of_groups, int number_of_parts);
+std::unique_ptr<android::fs_mgr::LpMetadata> addPartitions(int number_of_groups, int number_of_parts);
 
 namespace metadataio {
     bool operator==(const LpMetadataBlockDevice &a, const LpMetadataBlockDevice &b);