Dynamic Partitions: resizing utility 26/305426/4
authorJacek Kryszyn <j.kryszyn@samsung.com>
Wed, 31 Jan 2024 14:26:52 +0000 (15:26 +0100)
committerJacek Kryszyn <j.kryszyn@samsung.com>
Mon, 12 Feb 2024 14:52:26 +0000 (15:52 +0100)
This patch adds a utility called resize-dynparts which allows
modification of metadata stored on super in order to resize
dynamic partitions.

Change-Id: Idb22f21240a8716d5b303b6cfc456f17f399699e

67 files changed:
CMakeLists.target-build
data/40-upgrade.list.in
packaging/upgrade.spec
src/dynamic-partitions/CMakeLists.txt [new file with mode: 0755]
src/dynamic-partitions/liblp/CMakeLists.txt [new file with mode: 0644]
src/dynamic-partitions/liblp/README.md [new file with mode: 0644]
src/dynamic-partitions/liblp/builder.cpp [new file with mode: 0644]
src/dynamic-partitions/liblp/include/liblp/builder.h [new file with mode: 0644]
src/dynamic-partitions/liblp/include/liblp/liblp.h [new file with mode: 0644]
src/dynamic-partitions/liblp/include/liblp/metadata_format.h [new file with mode: 0644]
src/dynamic-partitions/liblp/reader.cpp [new file with mode: 0644]
src/dynamic-partitions/liblp/reader.h [new file with mode: 0644]
src/dynamic-partitions/liblp/utility.cpp [new file with mode: 0644]
src/dynamic-partitions/liblp/utility.h [new file with mode: 0644]
src/dynamic-partitions/liblp/writer.cpp [new file with mode: 0644]
src/dynamic-partitions/liblp/writer.h [new file with mode: 0644]
src/dynamic-partitions/parse-dynparts/.clang-format [new file with mode: 0644]
src/dynamic-partitions/parse-dynparts/.gitignore [new file with mode: 0644]
src/dynamic-partitions/parse-dynparts/.vscode/extensions.json [new file with mode: 0644]
src/dynamic-partitions/parse-dynparts/.vscode/settings.json [new file with mode: 0644]
src/dynamic-partitions/parse-dynparts/CMakeLists.txt [new file with mode: 0644]
src/dynamic-partitions/parse-dynparts/LICENSE [new file with mode: 0644]
src/dynamic-partitions/parse-dynparts/README.md [new file with mode: 0644]
src/dynamic-partitions/parse-dynparts/lib.cpp [new file with mode: 0644]
src/dynamic-partitions/parse-dynparts/lib.hpp [new file with mode: 0644]
src/dynamic-partitions/parse-dynparts/main.cpp [new file with mode: 0644]
src/dynamic-partitions/parse-dynparts/test/test.cpp [new file with mode: 0644]
src/dynamic-partitions/resize-dynparts/CMakeLists.txt [new file with mode: 0644]
src/dynamic-partitions/resize-dynparts/lib.cpp [new file with mode: 0644]
src/dynamic-partitions/resize-dynparts/lib.hpp [new file with mode: 0644]
src/dynamic-partitions/resize-dynparts/main.cpp [new file with mode: 0644]
src/dynamic-partitions/resize-dynparts/test/test.cpp [new file with mode: 0644]
src/dynamic-partitions/resize-dynparts/test/update_1.cfg [new file with mode: 0644]
src/dynamic-partitions/resize-dynparts/test/update_2.cfg [new file with mode: 0644]
src/dynamic-partitions/resize-dynparts/test/update_artificial.cfg [new file with mode: 0644]
src/dynamic-partitions/resize-dynparts/test/update_empty.cfg [new file with mode: 0644]
src/dynamic-partitions/resize-dynparts/test/update_malformed.cfg [new file with mode: 0644]
src/dynamic-partitions/resize-dynparts/test/update_real.cfg [new file with mode: 0644]
src/dynamic-partitions/testlib/CMakeLists.txt [new file with mode: 0644]
src/dynamic-partitions/testlib/generate_test_data.sh [new file with mode: 0755]
src/dynamic-partitions/testlib/generate_test_data_for_resizing.sh [new file with mode: 0755]
src/dynamic-partitions/testlib/metadataio.cpp [new file with mode: 0644]
src/dynamic-partitions/testlib/metadataio.h [new file with mode: 0644]
src/dynamic-partitions/testlib/super_dump.cpp [new file with mode: 0644]
src/parse-dynparts/.clang-format [deleted file]
src/parse-dynparts/.gitignore [deleted file]
src/parse-dynparts/.vscode/extensions.json [deleted file]
src/parse-dynparts/.vscode/settings.json [deleted file]
src/parse-dynparts/CMakeLists.txt [deleted file]
src/parse-dynparts/LICENSE [deleted file]
src/parse-dynparts/README.md [deleted file]
src/parse-dynparts/lib.cpp [deleted file]
src/parse-dynparts/lib.hpp [deleted file]
src/parse-dynparts/liblp/CMakeLists.txt [deleted file]
src/parse-dynparts/liblp/README.md [deleted file]
src/parse-dynparts/liblp/include/liblp/liblp.h [deleted file]
src/parse-dynparts/liblp/include/liblp/metadata_format.h [deleted file]
src/parse-dynparts/liblp/reader.cpp [deleted file]
src/parse-dynparts/liblp/reader.h [deleted file]
src/parse-dynparts/liblp/utility.cpp [deleted file]
src/parse-dynparts/liblp/utility.h [deleted file]
src/parse-dynparts/main.cpp [deleted file]
src/parse-dynparts/test/generate_test_data.sh [deleted file]
src/parse-dynparts/test/metadataio.cpp [deleted file]
src/parse-dynparts/test/metadataio.h [deleted file]
src/parse-dynparts/test/super_dump.cpp [deleted file]
src/parse-dynparts/test/test.cpp [deleted file]

index 53b554ecf2cbd8c022d3ba0df5cac5e69ad0da62..83f95d9735fbe88fc56391c3aad79d3e90bad807 100644 (file)
@@ -24,4 +24,4 @@ ADD_SUBDIRECTORY(src/upgrade-apply-deltafs)
 ADD_SUBDIRECTORY(src/blkid-print)
 ADD_SUBDIRECTORY(data)
 ADD_SUBDIRECTORY(scripts/rw-upgrade)
-ADD_SUBDIRECTORY(src/parse-dynparts)
+ADD_SUBDIRECTORY(src/dynamic-partitions)
index 41cb25465445f120ea658498b52c45d59f665345..30f7bace1279ac6bfe6a96a923dc131424857679 100644 (file)
@@ -9,6 +9,7 @@ ${CMAKE_INSTALL_LIBDIR}/libcrypto.so.1.1
 /bin/grep
 /bin/blkid-print
 /usr/sbin/parse-dynparts
+/bin/resize-dynparts
 "
 
 DIRECTORIES="
index ad2e8c1d8fededcf166847803ecbb23839437dad..d8ff70a1ae1b29ffb7f11227f837a2928be3d45f 100644 (file)
@@ -3,7 +3,7 @@
 
 Name:          upgrade
 Summary:       Upgrade support for Tizen
-Version:       8.1.0
+Version:       8.1.1
 Release:       0
 Group:         System
 License:       MIT, Apache-2.0
@@ -22,6 +22,7 @@ BuildRequires:  gtest-devel
 
 Requires:       upgrade-engine = %{version}-%{release}
 Requires:       parse-dynparts = %{version}-%{release}
+Requires:       resize-dynparts = %{version}-%{release}
 
 %description
 Metapackage requiring all upgrade-related packages on the platform.
@@ -51,6 +52,13 @@ This package provides utility needed to parse "super" on-disk format, and print
 out necessary device mapper maps, needed to later mount filesystems contained in
 dynamic partition area.
 
+%package -n resize-dynparts
+Summary:  Utility for modification of dynamic partitions size in super metadata
+
+%description -n resize-dynparts
+This package provides utility needed to modify metadata of a super partition by
+changing size of dynamic partitions.
+
 %prep
 %setup -q
 
@@ -185,4 +193,9 @@ fi
 %files -n parse-dynparts
 %manifest upgrade.manifest
 %license LICENSE.MIT
-%{_sbindir}/parse-dynparts
\ No newline at end of file
+%{_sbindir}/parse-dynparts
+
+%files -n resize-dynparts
+%manifest upgrade.manifest
+%license LICENSE.Apache-2.0
+%{_bindir}/resize-dynparts
\ No newline at end of file
diff --git a/src/dynamic-partitions/CMakeLists.txt b/src/dynamic-partitions/CMakeLists.txt
new file mode 100755 (executable)
index 0000000..344c58f
--- /dev/null
@@ -0,0 +1,7 @@
+add_subdirectory(liblp)
+ADD_SUBDIRECTORY(parse-dynparts)
+ADD_SUBDIRECTORY(resize-dynparts)
+
+# testlib contains a tool for creating test data manually once and is not used
+# during package building.
+#ADD_SUBDIRECTORY(testlib)
\ No newline at end of file
diff --git a/src/dynamic-partitions/liblp/CMakeLists.txt b/src/dynamic-partitions/liblp/CMakeLists.txt
new file mode 100644 (file)
index 0000000..ba3174a
--- /dev/null
@@ -0,0 +1,10 @@
+SET(CMAKE_CXX_STANDARD 17)
+SET(CMAKE_CXX_STANDARD_REQUIRED True)
+
+add_library(lp STATIC builder.cpp reader.cpp utility.cpp writer.cpp)
+
+target_include_directories(lp PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
+target_compile_options(lp PRIVATE -Wall -Wextra -pedantic -fPIE -Wno-stringop-truncation)
+
+find_package(OpenSSL REQUIRED)
+target_link_libraries(lp PRIVATE OpenSSL::Crypto)
\ No newline at end of file
diff --git a/src/dynamic-partitions/liblp/README.md b/src/dynamic-partitions/liblp/README.md
new file mode 100644 (file)
index 0000000..4d55aa1
--- /dev/null
@@ -0,0 +1,24 @@
+This code is based on https://github.com/tchebb/parse-android-dynparts
+which adapted `fs_mgr/liblp` from Android's `platform/system/core` project.
+`resize-dynparts` adds more code from `fs_mgr/liblp` in order to allow write
+operations on super metadata. Additional code comes from https://github.com/nmeum/android-tools
+and was also edited to be built independently from the rest of the AOSP codebase.
+
+android-tools commit 27118fb36c5042831cff35f80886dd1169c0b751
+parse-android-dynparts commit c8837c1cd0c4fbc29641980b71079fc4f3cabcc0 
+
+Below is the original description from https://github.com/tchebb/parse-android-dynparts
+
+liblp
+=====
+
+This code is based on `fs_mgr/liblp` from Android's `platform/system/core`
+project. However, I've removed most of the files and edited the ones that remain
+so that they build independently from the rest of the AOSP codebase. All
+functions still declared in the local copy of `include/liblp/liblp.h` ought to
+work. `CMakeLists.txt` is not from upstream and was authored by me to replace
+upstream's `Android.bp`.
+
+Currently derived from the upstream tree at [commit f9c36a2ca632][1].
+
+[1]: https://android.googlesource.com/platform/system/core/+/f9c36a2ca632fa88edba9c2c87f14f2aec1e7fd3/fs_mgr/liblp/
diff --git a/src/dynamic-partitions/liblp/builder.cpp b/src/dynamic-partitions/liblp/builder.cpp
new file mode 100644 (file)
index 0000000..ac609bf
--- /dev/null
@@ -0,0 +1,840 @@
+/*
+ * 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.
+ */
+
+#include "liblp/builder.h"
+
+#include <string.h>
+
+#include <algorithm>
+#include <limits>
+
+#include "liblp/liblp.h"
+#include "reader.h"
+#include "utility.h"
+
+namespace android {
+namespace fs_mgr {
+
+std::ostream& operator<<(std::ostream& os, const Extent& extent) {
+    switch (extent.GetExtentType()) {
+        case ExtentType::kZero: {
+            os << "type: Zero";
+            break;
+        }
+        case ExtentType::kLinear: {
+            auto linear_extent = static_cast<const LinearExtent*>(&extent);
+            os << "type: Linear, physical sectors: " << linear_extent->physical_sector()
+               << ", end sectors: " << linear_extent->end_sector();
+            break;
+        }
+    }
+    return os;
+}
+
+bool LinearExtent::AddTo(LpMetadata* out) const {
+    if (device_index_ >= out->block_devices.size()) {
+        LERROR << "Extent references unknown block device.";
+        return false;
+    }
+    out->extents.emplace_back(
+            LpMetadataExtent{num_sectors_, LP_TARGET_TYPE_LINEAR, physical_sector_, device_index_});
+    return true;
+}
+
+bool LinearExtent::operator==(const android::fs_mgr::Extent& other) const {
+    if (other.GetExtentType() != ExtentType::kLinear) {
+        return false;
+    }
+
+    auto other_ptr = static_cast<const LinearExtent*>(&other);
+    return num_sectors_ == other_ptr->num_sectors_ &&
+           physical_sector_ == other_ptr->physical_sector_ &&
+           device_index_ == other_ptr->device_index_;
+}
+
+bool LinearExtent::OverlapsWith(const LinearExtent& other) const {
+    if (device_index_ != other.device_index()) {
+        return false;
+    }
+    return physical_sector() < other.end_sector() && other.physical_sector() < end_sector();
+}
+
+bool LinearExtent::OverlapsWith(const Interval& interval) const {
+    if (device_index_ != interval.device_index) {
+        return false;
+    }
+    return physical_sector() < interval.end && interval.start < end_sector();
+}
+
+Interval LinearExtent::AsInterval() const {
+    return Interval(device_index(), physical_sector(), end_sector());
+}
+
+bool ZeroExtent::AddTo(LpMetadata* out) const {
+    out->extents.emplace_back(LpMetadataExtent{num_sectors_, LP_TARGET_TYPE_ZERO, 0, 0});
+    return true;
+}
+
+bool ZeroExtent::operator==(const android::fs_mgr::Extent& other) const {
+    return other.GetExtentType() == ExtentType::kZero && num_sectors_ == other.num_sectors();
+}
+
+Partition::Partition(std::string_view name, std::string_view group_name, uint32_t attributes)
+    : name_(name), group_name_(group_name), attributes_(attributes), size_(0) {}
+
+void Partition::AddExtent(std::unique_ptr<Extent>&& extent) {
+    size_ += extent->num_sectors() * LP_SECTOR_SIZE;
+
+    if (LinearExtent* new_extent = extent->AsLinearExtent()) {
+        if (!extents_.empty() && extents_.back()->AsLinearExtent()) {
+            LinearExtent* prev_extent = extents_.back()->AsLinearExtent();
+            if (prev_extent->end_sector() == new_extent->physical_sector() &&
+                prev_extent->device_index() == new_extent->device_index()) {
+                // If the previous extent can be merged into this new one, do so
+                // to avoid creating unnecessary extents.
+                extent = std::make_unique<LinearExtent>(
+                        prev_extent->num_sectors() + new_extent->num_sectors(),
+                        prev_extent->device_index(), prev_extent->physical_sector());
+                extents_.pop_back();
+            }
+        }
+    }
+    extents_.push_back(std::move(extent));
+}
+
+void Partition::RemoveExtents() {
+    size_ = 0;
+    extents_.clear();
+}
+
+void Partition::ShrinkTo(uint64_t aligned_size) {
+    if (aligned_size == 0) {
+        RemoveExtents();
+        return;
+    }
+
+    // Remove or shrink extents of any kind until the total partition size is
+    // equal to the requested size.
+    uint64_t sectors_to_remove = (size_ - aligned_size) / LP_SECTOR_SIZE;
+    while (sectors_to_remove) {
+        Extent* extent = extents_.back().get();
+        if (extent->num_sectors() > sectors_to_remove) {
+            size_ -= sectors_to_remove * LP_SECTOR_SIZE;
+            extent->set_num_sectors(extent->num_sectors() - sectors_to_remove);
+            break;
+        }
+        size_ -= (extent->num_sectors() * LP_SECTOR_SIZE);
+        sectors_to_remove -= extent->num_sectors();
+        extents_.pop_back();
+    }
+    //DCHECK(size_ == aligned_size);
+}
+
+Partition Partition::GetBeginningExtents(uint64_t aligned_size) const {
+    Partition p(name_, group_name_, attributes_);
+    for (const auto& extent : extents_) {
+        auto le = extent->AsLinearExtent();
+        if (le) {
+            p.AddExtent(std::make_unique<LinearExtent>(*le));
+        } else {
+            p.AddExtent(std::make_unique<ZeroExtent>(extent->num_sectors()));
+        }
+    }
+    p.ShrinkTo(aligned_size);
+    return p;
+}
+
+uint64_t Partition::BytesOnDisk() const {
+    uint64_t sectors = 0;
+    for (const auto& extent : extents_) {
+        if (!extent->AsLinearExtent()) {
+            continue;
+        }
+        sectors += extent->num_sectors();
+    }
+    return sectors * LP_SECTOR_SIZE;
+}
+
+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);
+    if (!metadata) {
+        return nullptr;
+    }
+    return New(*metadata.get());
+}
+
+std::unique_ptr<MetadataBuilder> MetadataBuilder::New(const LpMetadata& metadata) {
+    std::unique_ptr<MetadataBuilder> builder(new MetadataBuilder());
+    if (!builder->Init(metadata)) {
+        return nullptr;
+    }
+    return builder;
+}
+
+MetadataBuilder::MetadataBuilder() : auto_slot_suffixing_(false) {
+    memset(&geometry_, 0, sizeof(geometry_));
+    geometry_.magic = LP_METADATA_GEOMETRY_MAGIC;
+    geometry_.struct_size = sizeof(geometry_);
+
+    memset(&header_, 0, sizeof(header_));
+    header_.magic = LP_METADATA_HEADER_MAGIC;
+    header_.major_version = LP_METADATA_MAJOR_VERSION;
+    header_.minor_version = LP_METADATA_MINOR_VERSION_MIN;
+    header_.header_size = sizeof(LpMetadataHeaderV1_0);
+    header_.partitions.entry_size = sizeof(LpMetadataPartition);
+    header_.extents.entry_size = sizeof(LpMetadataExtent);
+    header_.groups.entry_size = sizeof(LpMetadataPartitionGroup);
+    header_.block_devices.entry_size = sizeof(LpMetadataBlockDevice);
+}
+
+bool MetadataBuilder::Init(const LpMetadata& metadata) {
+    geometry_ = metadata.geometry;
+    block_devices_ = metadata.block_devices;
+
+    // Bump the version as necessary to copy any newer fields.
+    if (metadata.header.minor_version >= LP_METADATA_VERSION_FOR_EXPANDED_HEADER) {
+        RequireExpandedMetadataHeader();
+        header_.flags = metadata.header.flags;
+    }
+
+    for (const auto& group : metadata.groups) {
+        std::string group_name = GetPartitionGroupName(group);
+        if (!AddGroup(group_name, group.maximum_size)) {
+            return false;
+        }
+    }
+
+    for (const auto& partition : metadata.partitions) {
+        std::string group_name = GetPartitionGroupName(metadata.groups[partition.group_index]);
+        Partition* builder =
+                AddPartition(GetPartitionName(partition), group_name, partition.attributes);
+        if (!builder) {
+            return false;
+        }
+        ImportExtents(builder, metadata, partition);
+    }
+    return true;
+}
+
+void MetadataBuilder::ImportExtents(Partition* dest, const LpMetadata& metadata,
+                                    const LpMetadataPartition& source) {
+    for (size_t i = 0; i < source.num_extents; i++) {
+        const LpMetadataExtent& extent = metadata.extents[source.first_extent_index + i];
+        if (extent.target_type == LP_TARGET_TYPE_LINEAR) {
+            auto copy = std::make_unique<LinearExtent>(extent.num_sectors, extent.target_source,
+                                                       extent.target_data);
+            dest->AddExtent(std::move(copy));
+        } else if (extent.target_type == LP_TARGET_TYPE_ZERO) {
+            auto copy = std::make_unique<ZeroExtent>(extent.num_sectors);
+            dest->AddExtent(std::move(copy));
+        }
+    }
+}
+
+bool MetadataBuilder::AddGroup(std::string_view group_name, uint64_t maximum_size) {
+    if (FindGroup(group_name)) {
+        LERROR << "Group already exists: " << group_name;
+        return false;
+    }
+    groups_.push_back(std::make_unique<PartitionGroup>(group_name, maximum_size));
+    return true;
+}
+
+Partition* MetadataBuilder::AddPartition(const std::string& name, uint32_t attributes) {
+    return AddPartition(name, kDefaultGroup, attributes);
+}
+
+Partition* MetadataBuilder::AddPartition(std::string_view name, std::string_view group_name,
+                                         uint32_t attributes) {
+    if (name.empty()) {
+        LERROR << "Partition must have a non-empty name.";
+        return nullptr;
+    }
+    if (FindPartition(name)) {
+        LERROR << "Attempting to create duplication partition with name: " << name;
+        return nullptr;
+    }
+    if (!FindGroup(group_name)) {
+        LERROR << "Could not find partition group: " << group_name;
+        return nullptr;
+    }
+    partitions_.push_back(std::make_unique<Partition>(name, group_name, attributes));
+    return partitions_.back().get();
+}
+
+Partition* MetadataBuilder::FindPartition(std::string_view name) const {
+    for (const auto& partition : partitions_) {
+        if (partition->name() == name) {
+            return partition.get();
+        }
+    }
+    return nullptr;
+}
+
+PartitionGroup* MetadataBuilder::FindGroup(std::string_view group_name) const {
+    for (const auto& group : groups_) {
+        if (group->name() == group_name) {
+            return group.get();
+        }
+    }
+    return nullptr;
+}
+
+uint64_t MetadataBuilder::TotalSizeOfGroup(PartitionGroup* group) const {
+    uint64_t total = 0;
+    for (const auto& partition : partitions_) {
+        if (partition->group_name() != group->name()) {
+            continue;
+        }
+        total += partition->BytesOnDisk();
+    }
+    return total;
+}
+
+void MetadataBuilder::RemovePartition(std::string_view name) {
+    for (auto iter = partitions_.begin(); iter != partitions_.end(); iter++) {
+        if ((*iter)->name() == name) {
+            partitions_.erase(iter);
+            return;
+        }
+    }
+}
+
+void MetadataBuilder::ExtentsToFreeList(const std::vector<Interval>& extents,
+                                        std::vector<Interval>* free_regions) const {
+    // Convert the extent list into a list of gaps between the extents; i.e.,
+    // the list of ranges that are free on the disk.
+    for (size_t i = 1; i < extents.size(); i++) {
+        const Interval& previous = extents[i - 1];
+        const Interval& current = extents[i];
+        //DCHECK(previous.device_index == current.device_index);
+
+        uint64_t aligned;
+        if (!AlignSector(block_devices_[current.device_index], previous.end, &aligned)) {
+            LERROR << "Sector " << previous.end << " caused integer overflow.";
+            continue;
+        }
+        if (aligned >= current.start) {
+            // There is no gap between these two extents, try the next one.
+            // Note that we check with >= instead of >, since alignment may
+            // bump the ending sector past the beginning of the next extent.
+            continue;
+        }
+
+        // The new interval represents the free space starting at the end of
+        // the previous interval, and ending at the start of the next interval.
+        free_regions->emplace_back(current.device_index, aligned, current.start);
+    }
+}
+
+auto MetadataBuilder::GetFreeRegions() const -> std::vector<Interval> {
+    std::vector<Interval> free_regions;
+
+    // Collect all extents in the partition table, per-device, then sort them
+    // by starting sector.
+    std::vector<std::vector<Interval>> device_extents(block_devices_.size());
+    for (const auto& partition : partitions_) {
+        for (const auto& extent : partition->extents()) {
+            LinearExtent* linear = extent->AsLinearExtent();
+            if (!linear) {
+                continue;
+            }
+            CHECK(linear->device_index() < device_extents.size());
+            auto& extents = device_extents[linear->device_index()];
+            extents.emplace_back(linear->device_index(), linear->physical_sector(),
+                                 linear->physical_sector() + extent->num_sectors());
+        }
+    }
+
+    // Add 0-length intervals for the first and last sectors. This will cause
+    // ExtentToFreeList() to treat the space in between as available.
+    for (size_t i = 0; i < device_extents.size(); i++) {
+        auto& extents = device_extents[i];
+        const auto& block_device = block_devices_[i];
+
+        uint64_t first_sector = block_device.first_logical_sector;
+        uint64_t last_sector = block_device.size / LP_SECTOR_SIZE;
+        extents.emplace_back(i, first_sector, first_sector);
+        extents.emplace_back(i, last_sector, last_sector);
+
+        std::sort(extents.begin(), extents.end());
+        ExtentsToFreeList(extents, &free_regions);
+    }
+    return free_regions;
+}
+
+bool MetadataBuilder::ValidatePartitionSizeChange(Partition* partition, uint64_t old_size,
+                                                  uint64_t new_size, bool force_check) {
+    PartitionGroup* group = FindGroup(partition->group_name());
+    CHECK(group);
+
+    if (!force_check && new_size <= old_size) {
+        return true;
+    }
+
+    // Figure out how much we need to allocate, and whether our group has
+    // enough space remaining.
+    uint64_t space_needed = new_size - old_size;
+    if (group->maximum_size() > 0) {
+        uint64_t group_size = TotalSizeOfGroup(group);
+        if (group_size >= group->maximum_size() ||
+            group->maximum_size() - group_size < space_needed) {
+            LERROR << "Partition " << partition->name() << " is part of group " << group->name()
+                   << " which does not have enough space free (" << space_needed << " requested, "
+                   << group_size << " used out of " << group->maximum_size() << ")";
+            return false;
+        }
+    }
+    return true;
+}
+
+Interval Interval::Intersect(const Interval& a, const Interval& b) {
+    Interval ret = a;
+    if (a.device_index != b.device_index) {
+        ret.start = ret.end = a.start;  // set length to 0 to indicate no intersection.
+        return ret;
+    }
+    ret.start = std::max(a.start, b.start);
+    ret.end = std::max(ret.start, std::min(a.end, b.end));
+    return ret;
+}
+
+std::vector<Interval> Interval::Intersect(const std::vector<Interval>& a,
+                                          const std::vector<Interval>& b) {
+    std::vector<Interval> ret;
+    for (const Interval& a_interval : a) {
+        for (const Interval& b_interval : b) {
+            auto intersect = Intersect(a_interval, b_interval);
+            if (intersect.length() > 0) ret.emplace_back(std::move(intersect));
+        }
+    }
+    return ret;
+}
+
+std::unique_ptr<Extent> Interval::AsExtent() const {
+    return std::make_unique<LinearExtent>(length(), device_index, start);
+}
+
+bool MetadataBuilder::GrowPartition(Partition* partition, uint64_t aligned_size,
+                                    const std::vector<Interval>& free_region_hint) {
+    uint64_t space_needed = aligned_size - partition->size();
+    uint64_t sectors_needed = space_needed / LP_SECTOR_SIZE;
+    //DCHECK(sectors_needed * LP_SECTOR_SIZE == space_needed);
+
+    std::vector<Interval> free_regions = GetFreeRegions();
+    if (!free_region_hint.empty())
+        free_regions = Interval::Intersect(free_regions, free_region_hint);
+
+    const uint64_t sectors_per_block = geometry_.logical_block_size / LP_SECTOR_SIZE;
+    //CHECK_NE(sectors_per_block, 0);
+    CHECK(sectors_per_block != 0);
+    CHECK(sectors_needed % sectors_per_block == 0);
+
+    if (IsABDevice() && ShouldHalveSuper() && GetPartitionSlotSuffix(partition->name()) == "_b") {
+        // Allocate "a" partitions top-down and "b" partitions bottom-up, to
+        // minimize fragmentation during OTA.
+        free_regions = PrioritizeSecondHalfOfSuper(free_regions);
+    }
+
+    // Note we store new extents in a temporary vector, and only commit them
+    // if we are guaranteed enough free space.
+    std::vector<std::unique_ptr<LinearExtent>> new_extents;
+
+    // If the last extent in the partition has a size < alignment, then the
+    // difference is unallocatable due to being misaligned. We peek for that
+    // case here to avoid wasting space.
+    if (auto extent = ExtendFinalExtent(partition, free_regions, sectors_needed)) {
+        sectors_needed -= extent->num_sectors();
+        new_extents.emplace_back(std::move(extent));
+    }
+
+    for (auto& region : free_regions) {
+        // Note: this comes first, since we may enter the loop not needing any
+        // more sectors.
+        if (!sectors_needed) {
+            break;
+        }
+
+        if (region.length() % sectors_per_block != 0) {
+            // This should never happen, because it would imply that we
+            // once allocated an extent that was not a multiple of the
+            // block size. That extent would be rejected by DM_TABLE_LOAD.
+            LERROR << "Region " << region.start << ".." << region.end
+                   << " is not a multiple of the block size, " << sectors_per_block;
+
+            // If for some reason the final region is mis-sized we still want
+            // to be able to grow partitions. So just to be safe, round the
+            // region down to the nearest block.
+            region.end = region.start + (region.length() / sectors_per_block) * sectors_per_block;
+            if (!region.length()) {
+                continue;
+            }
+        }
+
+        uint64_t sectors = std::min(sectors_needed, region.length());
+        CHECK(sectors % sectors_per_block == 0);
+
+        auto extent = std::make_unique<LinearExtent>(sectors, region.device_index, region.start);
+        new_extents.push_back(std::move(extent));
+        sectors_needed -= sectors;
+    }
+    if (sectors_needed) {
+        LERROR << "Not enough free space to expand partition: " << partition->name();
+        return false;
+    }
+
+    // Everything succeeded, so commit the new extents.
+    for (auto& extent : new_extents) {
+        partition->AddExtent(std::move(extent));
+    }
+    return true;
+}
+
+std::vector<Interval> MetadataBuilder::PrioritizeSecondHalfOfSuper(
+        const std::vector<Interval>& free_list) {
+    const auto& super = block_devices_[0];
+    uint64_t first_sector = super.first_logical_sector;
+    uint64_t last_sector = super.size / LP_SECTOR_SIZE;
+    uint64_t midpoint = first_sector + (last_sector - first_sector) / 2;
+
+    // Choose an aligned sector for the midpoint. This could lead to one half
+    // being slightly larger than the other, but this will not restrict the
+    // size of partitions (it might lead to one extra extent if "B" overflows).
+    if (!AlignSector(super, midpoint, &midpoint)) {
+        LERROR << "Unexpected integer overflow aligning midpoint " << midpoint;
+        return free_list;
+    }
+
+    std::vector<Interval> first_half;
+    std::vector<Interval> second_half;
+    for (const auto& region : free_list) {
+        // Note: deprioritze if not the main super partition. Even though we
+        // don't call this for retrofit devices, we will allow adding additional
+        // block devices on non-retrofit devices.
+        if (region.device_index != 0 || region.end <= midpoint) {
+            first_half.emplace_back(region);
+            continue;
+        }
+        if (region.start < midpoint && region.end > midpoint) {
+            // Split this into two regions.
+            first_half.emplace_back(region.device_index, region.start, midpoint);
+            second_half.emplace_back(region.device_index, midpoint, region.end);
+        } else {
+            second_half.emplace_back(region);
+        }
+    }
+    second_half.insert(second_half.end(), first_half.begin(), first_half.end());
+    return second_half;
+}
+
+std::unique_ptr<LinearExtent> MetadataBuilder::ExtendFinalExtent(
+        Partition* partition, const std::vector<Interval>& free_list,
+        uint64_t sectors_needed) const {
+    if (partition->extents().empty()) {
+        return nullptr;
+    }
+    LinearExtent* extent = partition->extents().back()->AsLinearExtent();
+    if (!extent) {
+        return nullptr;
+    }
+
+    // If the sector ends where the next aligned chunk begins, then there's
+    // no missing gap to try and allocate.
+    const auto& block_device = block_devices_[extent->device_index()];
+    uint64_t next_aligned_sector;
+    if (!AlignSector(block_device, extent->end_sector(), &next_aligned_sector)) {
+        LERROR << "Integer overflow aligning sector " << extent->end_sector();
+        return nullptr;
+    }
+    if (extent->end_sector() == next_aligned_sector) {
+        return nullptr;
+    }
+
+    uint64_t num_sectors = std::min(next_aligned_sector - extent->end_sector(), sectors_needed);
+    auto new_extent = std::make_unique<LinearExtent>(num_sectors, extent->device_index(),
+                                                     extent->end_sector());
+    if (IsAnyRegionAllocated(*new_extent.get()) ||
+        IsAnyRegionCovered(free_list, *new_extent.get())) {
+        LERROR << "Misaligned region " << new_extent->physical_sector() << ".."
+               << new_extent->end_sector() << " was allocated or marked allocatable.";
+        return nullptr;
+    }
+    return new_extent;
+}
+
+bool MetadataBuilder::IsAnyRegionCovered(const std::vector<Interval>& regions,
+                                         const LinearExtent& candidate) const {
+    for (const auto& region : regions) {
+        if (candidate.OverlapsWith(region)) {
+            return true;
+        }
+    }
+    return false;
+}
+
+bool MetadataBuilder::IsAnyRegionAllocated(const LinearExtent& candidate) const {
+    for (const auto& partition : partitions_) {
+        for (const auto& extent : partition->extents()) {
+            LinearExtent* linear = extent->AsLinearExtent();
+            if (!linear) {
+                continue;
+            }
+            if (linear->OverlapsWith(candidate)) {
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
+void MetadataBuilder::ShrinkPartition(Partition* partition, uint64_t aligned_size) {
+    partition->ShrinkTo(aligned_size);
+}
+
+std::unique_ptr<LpMetadata> MetadataBuilder::Export() {
+    if (!ValidatePartitionGroups()) {
+        return nullptr;
+    }
+
+    std::unique_ptr<LpMetadata> metadata = std::make_unique<LpMetadata>();
+    metadata->header = header_;
+    metadata->geometry = geometry_;
+
+    // Assign this early so the extent table can read it.
+    for (const auto& block_device : block_devices_) {
+        metadata->block_devices.emplace_back(block_device);
+        if (auto_slot_suffixing_) {
+            metadata->block_devices.back().flags |= LP_BLOCK_DEVICE_SLOT_SUFFIXED;
+        }
+    }
+
+    std::map<std::string, size_t> group_indices;
+    for (const auto& group : groups_) {
+        LpMetadataPartitionGroup out = {};
+
+        if (group->name().size() > sizeof(out.name)) {
+            LERROR << "Partition group name is too long: " << group->name();
+            return nullptr;
+        }
+        if (auto_slot_suffixing_ && group->name() != kDefaultGroup) {
+            out.flags |= LP_GROUP_SLOT_SUFFIXED;
+        }
+        strncpy(out.name, group->name().c_str(), sizeof(out.name));
+        out.maximum_size = group->maximum_size();
+
+        group_indices[group->name()] = metadata->groups.size();
+        metadata->groups.push_back(out);
+    }
+
+    // Flatten the partition and extent structures into an LpMetadata, which
+    // makes it very easy to validate, serialize, or pass on to device-mapper.
+    for (const auto& partition : partitions_) {
+        LpMetadataPartition part;
+        memset(&part, 0, sizeof(part));
+
+        if (partition->name().size() > sizeof(part.name)) {
+            LERROR << "Partition name is too long: " << partition->name();
+            return nullptr;
+        }
+        if (partition->attributes() & ~(LP_PARTITION_ATTRIBUTE_MASK)) {
+            LERROR << "Partition " << partition->name() << " has unsupported attribute.";
+            return nullptr;
+        }
+
+        if (partition->attributes() & LP_PARTITION_ATTRIBUTE_MASK_V1) {
+            static const uint16_t kMinVersion = LP_METADATA_VERSION_FOR_UPDATED_ATTR;
+            metadata->header.minor_version = std::max(metadata->header.minor_version, kMinVersion);
+        }
+
+        strncpy(part.name, partition->name().c_str(), sizeof(part.name));
+        part.first_extent_index = static_cast<uint32_t>(metadata->extents.size());
+        part.num_extents = static_cast<uint32_t>(partition->extents().size());
+        part.attributes = partition->attributes();
+        if (auto_slot_suffixing_) {
+            part.attributes |= LP_PARTITION_ATTR_SLOT_SUFFIXED;
+        }
+
+        auto iter = group_indices.find(partition->group_name());
+        if (iter == group_indices.end()) {
+            LERROR << "Partition " << partition->name() << " is a member of unknown group "
+                   << partition->group_name();
+            return nullptr;
+        }
+        part.group_index = iter->second;
+
+        for (const auto& extent : partition->extents()) {
+            if (!extent->AddTo(metadata.get())) {
+                return nullptr;
+            }
+        }
+        metadata->partitions.push_back(part);
+    }
+
+    metadata->header.partitions.num_entries = static_cast<uint32_t>(metadata->partitions.size());
+    metadata->header.extents.num_entries = static_cast<uint32_t>(metadata->extents.size());
+    metadata->header.groups.num_entries = static_cast<uint32_t>(metadata->groups.size());
+    metadata->header.block_devices.num_entries =
+            static_cast<uint32_t>(metadata->block_devices.size());
+    return metadata;
+}
+
+void MetadataBuilder::RequireExpandedMetadataHeader() {
+    if (header_.minor_version >= LP_METADATA_VERSION_FOR_EXPANDED_HEADER) {
+        return;
+    }
+    header_.minor_version = LP_METADATA_VERSION_FOR_EXPANDED_HEADER;
+    header_.header_size = sizeof(LpMetadataHeaderV1_2);
+}
+
+bool MetadataBuilder::AlignSector(const LpMetadataBlockDevice& block_device, uint64_t sector,
+                                  uint64_t* out) const {
+    // Note: when reading alignment info from the Kernel, we don't assume it
+    // is aligned to the sector size, so we round up to the nearest sector.
+    uint64_t lba = sector * LP_SECTOR_SIZE;
+    if (!AlignTo(lba, block_device.alignment, out)) {
+        return false;
+    }
+    if (!AlignTo(*out, LP_SECTOR_SIZE, out)) {
+        return false;
+    }
+    *out /= LP_SECTOR_SIZE;
+    return true;
+}
+
+bool MetadataBuilder::ResizePartition(Partition* partition, uint64_t requested_size,
+                                      const std::vector<Interval>& free_region_hint) {
+    // Align the space needed up to the nearest sector.
+    uint64_t aligned_size;
+    if (!AlignTo(requested_size, geometry_.logical_block_size, &aligned_size)) {
+        LERROR << "Cannot resize partition " << partition->name() << " to " << requested_size
+               << " bytes; integer overflow.";
+        return false;
+    }
+    uint64_t old_size = partition->size();
+
+    if (!ValidatePartitionSizeChange(partition, old_size, aligned_size, false)) {
+        return false;
+    }
+
+    if (aligned_size > old_size) {
+        if (!GrowPartition(partition, aligned_size, free_region_hint)) {
+            return false;
+        }
+    } else if (aligned_size < partition->size()) {
+        ShrinkPartition(partition, aligned_size);
+    }
+
+    if (partition->size() != old_size) {
+        LINFO << "Partition " << partition->name() << " will resize from " << old_size
+              << " bytes to " << aligned_size << " bytes";
+    }
+    return true;
+}
+
+std::vector<std::string> MetadataBuilder::ListGroups() const {
+    std::vector<std::string> names;
+    for (const auto& group : groups_) {
+        names.emplace_back(group->name());
+    }
+    return names;
+}
+
+void MetadataBuilder::RemoveGroupAndPartitions(std::string_view group_name) {
+    if (group_name == kDefaultGroup) {
+        // Cannot remove the default group.
+        return;
+    }
+    std::vector<std::string> partition_names;
+    for (const auto& partition : partitions_) {
+        if (partition->group_name() == group_name) {
+            partition_names.emplace_back(partition->name());
+        }
+    }
+
+    for (const auto& partition_name : partition_names) {
+        RemovePartition(partition_name);
+    }
+    for (auto iter = groups_.begin(); iter != groups_.end(); iter++) {
+        if ((*iter)->name() == group_name) {
+            groups_.erase(iter);
+            break;
+        }
+    }
+}
+
+//TODO: what to do about it?
+bool MetadataBuilder::IsABDevice() {
+    return true;
+    //return !IPropertyFetcher::GetInstance()->GetProperty("ro.boot.slot_suffix", "").empty();
+}
+
+//TODO: what to do about it?
+bool MetadataBuilder::ShouldHalveSuper() const {
+    return true;
+    /*
+
+    return GetBlockDevicePartitionName(0) == LP_METADATA_DEFAULT_PARTITION_NAME &&
+           !IPropertyFetcher::GetInstance()->GetBoolProperty("ro.virtual_ab.enabled", false);
+    */
+}
+
+bool MetadataBuilder::ChangePartitionGroup(Partition* partition, std::string_view group_name) {
+    if (!FindGroup(group_name)) {
+        LERROR << "Partition cannot change to unknown group: " << group_name;
+        return false;
+    }
+    partition->set_group_name(group_name);
+    return true;
+}
+
+bool MetadataBuilder::ValidatePartitionGroups() const {
+    for (const auto& group : groups_) {
+        if (!group->maximum_size()) {
+            continue;
+        }
+        uint64_t used = TotalSizeOfGroup(group.get());
+        if (used > group->maximum_size()) {
+            LERROR << "Partition group " << group->name() << " exceeds maximum size (" << used
+                   << " bytes used, maximum " << group->maximum_size() << ")";
+            return false;
+        }
+    }
+    return true;
+}
+
+bool MetadataBuilder::ChangeGroupSize(const std::string& group_name, uint64_t maximum_size) {
+    if (group_name == kDefaultGroup) {
+        LERROR << "Cannot change the size of the default group";
+        return false;
+    }
+    PartitionGroup* group = FindGroup(group_name);
+    if (!group) {
+        LERROR << "Cannot change size of unknown partition group: " << group_name;
+        return false;
+    }
+    group->set_maximum_size(maximum_size);
+    return true;
+}
+
+std::string MetadataBuilder::GetBlockDevicePartitionName(uint64_t index) const {
+    return index < block_devices_.size()
+                   ? android::fs_mgr::GetBlockDevicePartitionName(block_devices_[index])
+                   : "";
+}
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/src/dynamic-partitions/liblp/include/liblp/builder.h b/src/dynamic-partitions/liblp/include/liblp/builder.h
new file mode 100644 (file)
index 0000000..7f0bb77
--- /dev/null
@@ -0,0 +1,331 @@
+//
+// 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.
+//
+
+#ifndef LIBLP_METADATA_BUILDER_H
+#define LIBLP_METADATA_BUILDER_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <map>
+#include <memory>
+#include <optional>
+#include <set>
+#include <string_view>
+
+#include "liblp.h"
+
+namespace android {
+namespace fs_mgr {
+
+class LinearExtent;
+struct Interval;
+
+// By default, partitions are aligned on a 1MiB boundary.
+static constexpr uint32_t kDefaultPartitionAlignment = 1024 * 1024;
+static constexpr uint32_t kDefaultBlockSize = 4096;
+
+// Name of the default group in a metadata.
+static constexpr std::string_view kDefaultGroup = "default";
+
+enum class ExtentType {
+    kZero,
+    kLinear,
+};
+
+// Abstraction around dm-targets that can be encoded into logical partition tables.
+class Extent {
+  public:
+    explicit Extent(uint64_t num_sectors) : num_sectors_(num_sectors) {}
+    virtual ~Extent() {}
+
+    virtual bool AddTo(LpMetadata* out) const = 0;
+    virtual LinearExtent* AsLinearExtent() { return nullptr; }
+    virtual ExtentType GetExtentType() const = 0;
+
+    virtual bool operator==(const Extent& other) const = 0;
+    virtual bool operator!=(const Extent& other) const { return !(*this == other); }
+
+    uint64_t num_sectors() const { return num_sectors_; }
+    void set_num_sectors(uint64_t num_sectors) { num_sectors_ = num_sectors; }
+
+  protected:
+    uint64_t num_sectors_;
+};
+
+std::ostream& operator<<(std::ostream& os, const Extent& extent);
+
+// This corresponds to a dm-linear target.
+class LinearExtent final : public Extent {
+  public:
+    LinearExtent(uint64_t num_sectors, uint32_t device_index, uint64_t physical_sector)
+        : Extent(num_sectors), device_index_(device_index), physical_sector_(physical_sector) {}
+
+    bool AddTo(LpMetadata* metadata) const override;
+    LinearExtent* AsLinearExtent() override { return this; }
+    ExtentType GetExtentType() const override { return ExtentType::kLinear; }
+
+    bool operator==(const Extent& other) const override;
+
+    uint64_t physical_sector() const { return physical_sector_; }
+    uint64_t end_sector() const { return physical_sector_ + num_sectors_; }
+    uint32_t device_index() const { return device_index_; }
+
+    bool OverlapsWith(const LinearExtent& other) const;
+    bool OverlapsWith(const Interval& interval) const;
+
+    Interval AsInterval() const;
+
+  private:
+    uint32_t device_index_;
+    uint64_t physical_sector_;
+};
+
+// This corresponds to a dm-zero target.
+class ZeroExtent final : public Extent {
+  public:
+    explicit ZeroExtent(uint64_t num_sectors) : Extent(num_sectors) {}
+
+    bool AddTo(LpMetadata* out) const override;
+    ExtentType GetExtentType() const override { return ExtentType::kZero; }
+
+    bool operator==(const Extent& other) const override;
+};
+
+class PartitionGroup final {
+    friend class MetadataBuilder;
+
+  public:
+    explicit PartitionGroup(std::string_view name, uint64_t maximum_size)
+        : name_(name), maximum_size_(maximum_size) {}
+
+    const std::string& name() const { return name_; }
+    uint64_t maximum_size() const { return maximum_size_; }
+
+  private:
+    void set_maximum_size(uint64_t maximum_size) { maximum_size_ = maximum_size; }
+
+    std::string name_;
+    uint64_t maximum_size_;
+};
+
+class Partition final {
+    friend class MetadataBuilder;
+
+  public:
+    Partition(std::string_view name, std::string_view group_name, uint32_t attributes);
+
+    // Add a raw extent.
+    void AddExtent(std::unique_ptr<Extent>&& extent);
+
+    // Remove all extents from this partition.
+    void RemoveExtents();
+
+    // Compute the size used by linear extents. This is the same as size(),
+    // but does not factor in extents which do not take up space.
+    uint64_t BytesOnDisk() const;
+
+    const std::string& name() const { return name_; }
+    const std::string& group_name() const { return group_name_; }
+    uint32_t attributes() const { return attributes_; }
+    void set_attributes(uint32_t attributes) { attributes_ = attributes; }
+    const std::vector<std::unique_ptr<Extent>>& extents() const { return extents_; }
+    uint64_t size() const { return size_; }
+
+    // Return a copy of *this, but with extents that includes only the first
+    // |aligned_size| bytes. |aligned_size| should be aligned to
+    // logical_block_size() of the MetadataBuilder that this partition belongs
+    // to.
+    Partition GetBeginningExtents(uint64_t aligned_size) const;
+
+  private:
+    void ShrinkTo(uint64_t aligned_size);
+    void set_group_name(std::string_view group_name) { group_name_ = group_name; }
+
+    std::string name_;
+    std::string group_name_;
+    std::vector<std::unique_ptr<Extent>> extents_;
+    uint32_t attributes_;
+    uint64_t size_;
+};
+
+// An interval in the metadata. This is similar to a LinearExtent with one difference.
+// LinearExtent represents a "used" region in the metadata, while Interval can also represent
+// an "unused" region.
+struct Interval {
+    uint32_t device_index;
+    uint64_t start;
+    uint64_t end;
+
+    Interval(uint32_t device_index, uint64_t start, uint64_t end)
+        : device_index(device_index), start(start), end(end) {}
+    uint64_t length() const { return end - start; }
+
+    // Note: the device index is not included in sorting (intervals are
+    // sorted in per-device lists).
+    bool operator<(const Interval& other) const {
+        return (start == other.start) ? end < other.end : start < other.start;
+    }
+
+    std::unique_ptr<Extent> AsExtent() const;
+
+    // Intersect |a| with |b|.
+    // If no intersection, result has 0 length().
+    static Interval Intersect(const Interval& a, const Interval& b);
+
+    // Intersect two lists of intervals, and store result to |a|.
+    static std::vector<Interval> Intersect(const std::vector<Interval>& a,
+                                           const std::vector<Interval>& b);
+};
+
+class MetadataBuilder {
+  public:
+    // 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.
+    static std::unique_ptr<MetadataBuilder> New(const std::string& super_partition,
+                                                uint32_t slot_number);
+
+    // Import an existing table for modification. If the table is not valid, for
+    // example it contains duplicate partition names, then nullptr is returned.
+    //
+    // If an IPartitionOpener is specified, then block device informatiom will
+    // be updated.
+    static std::unique_ptr<MetadataBuilder> New(const LpMetadata& metadata);
+
+    // 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.
+    //
+    // This can fail and return false if the group already exists.
+    bool AddGroup(std::string_view group_name, uint64_t maximum_size);
+
+    // Export metadata so it can be serialized to an image, to disk, or mounted
+    // via device-mapper.
+    std::unique_ptr<LpMetadata> Export();
+
+    // Add a partition, returning a handle so it can be sized as needed. If a
+    // partition with the given name already exists, nullptr is returned.
+    Partition* AddPartition(std::string_view name, std::string_view group_name,
+                            uint32_t attributes);
+
+    // Same as AddPartition above, but uses the default partition group which
+    // has no size restrictions.
+    Partition* AddPartition(const std::string& name, uint32_t attributes);
+
+    // Delete a partition by name if it exists.
+    void RemovePartition(std::string_view name);
+
+    // Find a partition by name. If no partition is found, nullptr is returned.
+    Partition* FindPartition(std::string_view name) const;
+
+    // Find a group by name. If no group is found, nullptr is returned.
+    PartitionGroup* FindGroup(std::string_view name) const;
+
+    // Grow or shrink a partition to the requested size. This size will be
+    // rounded UP to the nearest block (512 bytes).
+    //
+    // When growing a partition, a greedy algorithm is used to find free gaps
+    // in the partition table and allocate them. If not enough space can be
+    // allocated, false is returned, and the parition table will not be
+    // modified.
+    //
+    // Note, this is an in-memory operation, and it does not alter the
+    // underlying filesystem or contents of the partition on disk.
+    //
+    // If |free_region_hint| is not empty, it will only try to allocate extents
+    // in regions within the list.
+    bool ResizePartition(Partition* partition, uint64_t requested_size,
+                         const std::vector<Interval>& free_region_hint = {});
+
+    // Return the list of partitions belonging to a group.
+    std::vector<Partition*> ListPartitionsInGroup(std::string_view group_name);
+
+    // Changes a partition's group. Size constraints will not be checked until
+    // the metadata is exported, to avoid errors during potential group and
+    // size shuffling operations. This will return false if the new group does
+    // not exist.
+    bool ChangePartitionGroup(Partition* partition, std::string_view group_name);
+
+    // Changes the size of a partition group. Size constraints will not be
+    // checked until metadata is exported, to avoid errors during group
+    // reshuffling. This will return false if the group does not exist, or if
+    // the group name is "default".
+    bool ChangeGroupSize(const std::string& group_name, uint64_t maximum_size);
+
+    // Return a list of all group names.
+    std::vector<std::string> ListGroups() const;
+
+    // Remove all partitions belonging to a group, then remove the group.
+    void RemoveGroupAndPartitions(std::string_view group_name);
+
+    // Require the expanded metadata header. This is exposed for testing, and
+    // is normally only called as needed by other methods.
+    void RequireExpandedMetadataHeader();
+
+    // Return the name of the block device at |index|.
+    std::string GetBlockDevicePartitionName(uint64_t index) const;
+
+    // Return the list of free regions not occupied by extents in the metadata.
+    std::vector<Interval> GetFreeRegions() const;
+
+  private:
+    MetadataBuilder();
+    MetadataBuilder(const MetadataBuilder&) = delete;
+    MetadataBuilder(MetadataBuilder&&) = delete;
+    MetadataBuilder& operator=(const MetadataBuilder&) = delete;
+    MetadataBuilder& operator=(MetadataBuilder&&) = delete;
+    bool Init(const LpMetadata& metadata);
+    bool GrowPartition(Partition* partition, uint64_t aligned_size,
+                       const std::vector<Interval>& free_region_hint);
+    void ShrinkPartition(Partition* partition, uint64_t aligned_size);
+    bool AlignSector(const LpMetadataBlockDevice& device, uint64_t sector, uint64_t* out) const;
+    uint64_t TotalSizeOfGroup(PartitionGroup* group) const;
+    bool ValidatePartitionSizeChange(Partition* partition, uint64_t old_size, uint64_t new_size,
+                                     bool force_check);
+    void ImportExtents(Partition* dest, const LpMetadata& metadata,
+                       const LpMetadataPartition& source);
+
+    // Return true if the device is an AB device.
+    static bool IsABDevice();
+
+    // Return true if _b partitions should be prioritized at the second half of the device.
+    bool ShouldHalveSuper() const;
+
+    bool ValidatePartitionGroups() const;
+
+    bool IsAnyRegionCovered(const std::vector<Interval>& regions,
+                            const LinearExtent& candidate) const;
+    bool IsAnyRegionAllocated(const LinearExtent& candidate) const;
+    void ExtentsToFreeList(const std::vector<Interval>& extents,
+                           std::vector<Interval>* free_regions) const;
+    std::vector<Interval> PrioritizeSecondHalfOfSuper(const std::vector<Interval>& free_list);
+    std::unique_ptr<LinearExtent> ExtendFinalExtent(Partition* partition,
+                                                    const std::vector<Interval>& free_list,
+                                                    uint64_t sectors_needed) const;
+
+    LpMetadataGeometry geometry_;
+    LpMetadataHeader header_;
+    std::vector<std::unique_ptr<Partition>> partitions_;
+    std::vector<std::unique_ptr<PartitionGroup>> groups_;
+    std::vector<LpMetadataBlockDevice> block_devices_;
+    bool auto_slot_suffixing_;
+};
+
+}  // namespace fs_mgr
+}  // namespace android
+
+#endif /* LIBLP_METADATA_BUILDER_H */
diff --git a/src/dynamic-partitions/liblp/include/liblp/liblp.h b/src/dynamic-partitions/liblp/include/liblp/liblp.h
new file mode 100644 (file)
index 0000000..c6b9b1b
--- /dev/null
@@ -0,0 +1,82 @@
+//
+// 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.
+//
+
+#ifndef LIBLP_LIBLP_H
+#define LIBLP_LIBLP_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <map>
+#include <memory>
+#include <string>
+
+#include "metadata_format.h"
+
+namespace android {
+namespace fs_mgr {
+
+// Helper structure for easily interpreting deserialized metadata, or
+// re-serializing metadata.
+struct LpMetadata {
+    LpMetadataGeometry geometry;
+    LpMetadataHeader header;
+    std::vector<LpMetadataPartition> partitions;
+    std::vector<LpMetadataExtent> extents;
+    std::vector<LpMetadataPartitionGroup> groups;
+    std::vector<LpMetadataBlockDevice> block_devices;
+};
+
+// Update the partition table for a given metadata slot number. False is
+// returned if an error occurs, which can include:
+//  - Invalid slot number.
+//  - I/O error.
+//  - Corrupt or missing metadata geometry on disk.
+//  - Incompatible geometry.
+bool UpdatePartitionTable(const std::string& super_partition, const LpMetadata& metadata,
+                          uint32_t slot_number);
+// Read logical partition metadata from its predetermined location on a block
+// device. If readback fails, we also attempt to load from a backup copy.
+std::unique_ptr<LpMetadata> ReadMetadata(const std::string& super_partition, uint32_t slot_number);
+
+// Helper to extract safe C++ strings from partition info.
+std::string GetPartitionName(const LpMetadataPartition& partition);
+std::string GetPartitionGroupName(const LpMetadataPartitionGroup& group);
+std::string GetBlockDevicePartitionName(const LpMetadataBlockDevice& block_device);
+
+// Return the block device that houses the super partition metadata; returns
+// null on failure.
+const LpMetadataBlockDevice* GetMetadataSuperBlockDevice(const LpMetadata& metadata);
+
+// Return the total size of all partitions comprising the super partition.
+uint64_t GetTotalSuperPartitionSize(const LpMetadata& metadata);
+
+// Get the list of block device names required by the given metadata.
+std::vector<std::string> GetBlockDevicePartitionNames(const LpMetadata& metadata);
+
+// Slot suffix helpers.
+uint32_t SlotNumberForSlotSuffix(const std::string& suffix);
+std::string SlotSuffixForSlotNumber(uint32_t slot_number);
+std::string GetPartitionSlotSuffix(const std::string& partition_name);
+
+// Helpers for common functions.
+const LpMetadataPartition* FindPartition(const LpMetadata& metadata, const std::string& name);
+uint64_t GetPartitionSize(const LpMetadata& metadata, const LpMetadataPartition& partition);
+
+}  // namespace fs_mgr
+}  // namespace android
+
+#endif  // LIBLP_LIBLP_H
diff --git a/src/dynamic-partitions/liblp/include/liblp/metadata_format.h b/src/dynamic-partitions/liblp/include/liblp/metadata_format.h
new file mode 100644 (file)
index 0000000..41d8b0c
--- /dev/null
@@ -0,0 +1,403 @@
+/*
+ * 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.
+ */
+
+#ifndef LOGICAL_PARTITION_METADATA_FORMAT_H_
+#define LOGICAL_PARTITION_METADATA_FORMAT_H_
+
+#ifdef __cplusplus
+#include <string>
+#include <vector>
+#endif
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Magic signature for LpMetadataGeometry. */
+#define LP_METADATA_GEOMETRY_MAGIC 0x616c4467
+
+/* Space reserved for geometry information. */
+#define LP_METADATA_GEOMETRY_SIZE 4096
+
+/* Magic signature for LpMetadataHeader. */
+#define LP_METADATA_HEADER_MAGIC 0x414C5030
+
+/* Current metadata version. */
+#define LP_METADATA_MAJOR_VERSION 10
+#define LP_METADATA_MINOR_VERSION_MIN 0
+#define LP_METADATA_MINOR_VERSION_MAX 2
+
+/* Metadata version needed to use the UPDATED partition attribute. */
+#define LP_METADATA_VERSION_FOR_UPDATED_ATTR 1
+
+/* Metadata version needed for the new expanded header struct. */
+#define LP_METADATA_VERSION_FOR_EXPANDED_HEADER 2
+
+/* Attributes for the LpMetadataPartition::attributes field.
+ *
+ * READONLY - The partition should not be considered writable. When used with
+ * device mapper, the block device will be created as read-only.
+ */
+#define LP_PARTITION_ATTR_NONE 0x0
+#define LP_PARTITION_ATTR_READONLY (1 << 0)
+
+/* This flag is only intended to be used with super_empty.img and super.img on
+ * retrofit devices. On these devices there are A and B super partitions, and
+ * we don't know ahead of time which slot the image will be applied to.
+ *
+ * If set, the partition name needs a slot suffix applied. The slot suffix is
+ * determined by the metadata slot number (0 = _a, 1 = _b).
+ */
+#define LP_PARTITION_ATTR_SLOT_SUFFIXED (1 << 1)
+
+/* This flag is applied automatically when using MetadataBuilder::NewForUpdate.
+ * It signals that the partition was created (or modified) for a snapshot-based
+ * update. If this flag is not present, the partition was likely flashed via
+ * fastboot.
+ */
+#define LP_PARTITION_ATTR_UPDATED (1 << 2)
+
+/* This flag marks a partition as disabled. It should not be used or mapped. */
+#define LP_PARTITION_ATTR_DISABLED (1 << 3)
+
+/* Mask that defines all valid attributes. When changing this, make sure to
+ * update ParseMetadata().
+ */
+#define LP_PARTITION_ATTRIBUTE_MASK_V0 \
+    (LP_PARTITION_ATTR_READONLY | LP_PARTITION_ATTR_SLOT_SUFFIXED)
+#define LP_PARTITION_ATTRIBUTE_MASK_V1 (LP_PARTITION_ATTR_UPDATED | LP_PARTITION_ATTR_DISABLED)
+#define LP_PARTITION_ATTRIBUTE_MASK \
+    (LP_PARTITION_ATTRIBUTE_MASK_V0 | LP_PARTITION_ATTRIBUTE_MASK_V1)
+
+/* Default name of the physical partition that holds logical partition entries.
+ * The layout of this partition will look like:
+ *
+ *     +--------------------+
+ *     | Disk Geometry      |
+ *     +--------------------+
+ *     | Geometry Backup    |
+ *     +--------------------+
+ *     | Metadata           |
+ *     +--------------------+
+ *     | Backup Metadata    |
+ *     +--------------------+
+ *     | Logical Partitions |
+ *     +--------------------+
+ */
+#define LP_METADATA_DEFAULT_PARTITION_NAME "super"
+
+/* Size of a sector is always 512 bytes for compatibility with the Linux kernel. */
+#define LP_SECTOR_SIZE 512
+
+/* Amount of space reserved at the start of every super partition to avoid
+ * creating an accidental boot sector.
+ */
+#define LP_PARTITION_RESERVED_BYTES 4096
+
+/* This structure is stored at block 0 in the first 4096 bytes of the
+ * partition, and again in the following block. It is never modified and
+ * describes how logical partition information can be located.
+ */
+typedef struct LpMetadataGeometry {
+    /*  0: Magic signature (LP_METADATA_GEOMETRY_MAGIC). */
+    uint32_t magic;
+
+    /*  4: Size of the LpMetadataGeometry struct. */
+    uint32_t struct_size;
+
+    /*  8: SHA256 checksum of this struct, with this field set to 0. */
+    uint8_t checksum[32];
+
+    /* 40: Maximum amount of space a single copy of the metadata can use. This
+     * must be a multiple of LP_SECTOR_SIZE.
+     */
+    uint32_t metadata_max_size;
+
+    /* 44: Number of copies of the metadata to keep. For A/B devices, this
+     * will be 2. For an A/B/C device, it would be 3, et cetera. For Non-A/B
+     * it will be 1. A backup copy of each slot is kept, so if this is "2",
+     * there will be four copies total.
+     */
+    uint32_t metadata_slot_count;
+
+    /* 48: Logical block size. This is the minimal alignment for partition and
+     * extent sizes, and it must be a multiple of LP_SECTOR_SIZE. Note that
+     * this must be equal across all LUNs that comprise the super partition,
+     * and thus this field is stored in the geometry, not per-device.
+     */
+    uint32_t logical_block_size;
+} __attribute__((packed)) LpMetadataGeometry;
+
+/* The logical partition metadata has a number of tables; they are described
+ * in the header via the following structure.
+ *
+ * The size of the table can be computed by multiplying entry_size by
+ * num_entries, and the result must not overflow a 32-bit signed integer.
+ */
+typedef struct LpMetadataTableDescriptor {
+    /*  0: Location of the table, relative to end of the metadata header. */
+    uint32_t offset;
+    /*  4: Number of entries in the table. */
+    uint32_t num_entries;
+    /*  8: Size of each entry in the table, in bytes. */
+    uint32_t entry_size;
+} __attribute__((packed)) LpMetadataTableDescriptor;
+
+/* Binary format for the header of the logical partition metadata format.
+ *
+ * The format has three sections. The header must occur first, and the
+ * proceeding tables may be placed in any order after.
+ *
+ *  +-----------------------------------------+
+ *  | Header data - fixed size                |
+ *  +-----------------------------------------+
+ *  | Partition table - variable size         |
+ *  +-----------------------------------------+
+ *  | Partition table extents - variable size |
+ *  +-----------------------------------------+
+ *
+ * The "Header" portion is described by LpMetadataHeader. It will always
+ * precede the other three blocks.
+ *
+ * All fields are stored in little-endian byte order when serialized.
+ *
+ * This struct is versioned; see the |major_version| and |minor_version|
+ * fields.
+ */
+typedef struct LpMetadataHeader {
+    /*  0: Four bytes equal to LP_METADATA_HEADER_MAGIC. */
+    uint32_t magic;
+
+    /*  4: Version number required to read this metadata. If the version is not
+     * equal to the library version, the metadata should be considered
+     * incompatible.
+     */
+    uint16_t major_version;
+
+    /*  6: Minor version. A library supporting newer features should be able to
+     * read metadata with an older minor version. However, an older library
+     * should not support reading metadata if its minor version is higher.
+     */
+    uint16_t minor_version;
+
+    /*  8: The size of this header struct. */
+    uint32_t header_size;
+
+    /* 12: SHA256 checksum of the header, up to |header_size| bytes, computed as
+     * if this field were set to 0.
+     */
+    uint8_t header_checksum[32];
+
+    /* 44: The total size of all tables. This size is contiguous; tables may not
+     * have gaps in between, and they immediately follow the header.
+     */
+    uint32_t tables_size;
+
+    /* 48: SHA256 checksum of all table contents. */
+    uint8_t tables_checksum[32];
+
+    /* 80: Partition table descriptor. */
+    LpMetadataTableDescriptor partitions;
+    /* 92: Extent table descriptor. */
+    LpMetadataTableDescriptor extents;
+    /* 104: Updateable group descriptor. */
+    LpMetadataTableDescriptor groups;
+    /* 116: Block device table. */
+    LpMetadataTableDescriptor block_devices;
+
+    /* Everything past here is header version 1.2+, and is only included if
+     * needed. When liblp supporting >= 1.2 reads a < 1.2 header, it must
+     * zero these additional fields.
+     */
+
+    /* 128: See LP_HEADER_FLAG_ constants for possible values. Header flags are
+     * independent of the version number and intended to be informational only.
+     * New flags can be added without bumping the version.
+     */
+    uint32_t flags;
+
+    /* 132: Reserved (zero), pad to 256 bytes. */
+    uint8_t reserved[124];
+} __attribute__((packed)) LpMetadataHeader;
+
+/* This device uses Virtual A/B. Note that on retrofit devices, the expanded
+ * header may not be present.
+ */
+#define LP_HEADER_FLAG_VIRTUAL_AB_DEVICE 0x1
+
+/* This struct defines a logical partition entry, similar to what would be
+ * present in a GUID Partition Table.
+ */
+typedef struct LpMetadataPartition {
+    /*  0: Name of this partition in ASCII characters. Any unused characters in
+     * the buffer must be set to 0. Characters may only be alphanumeric or _.
+     * The name must include at least one ASCII character, and it must be unique
+     * across all partition names. The length (36) is the same as the maximum
+     * length of a GPT partition name.
+     */
+    char name[36];
+
+    /* 36: Attributes for the partition (see LP_PARTITION_ATTR_* flags above). */
+    uint32_t attributes;
+
+    /* 40: Index of the first extent owned by this partition. The extent will
+     * start at logical sector 0. Gaps between extents are not allowed.
+     */
+    uint32_t first_extent_index;
+
+    /* 44: Number of extents in the partition. Every partition must have at
+     * least one extent.
+     */
+    uint32_t num_extents;
+
+    /* 48: Group this partition belongs to. */
+    uint32_t group_index;
+} __attribute__((packed)) LpMetadataPartition;
+
+/* This extent is a dm-linear target, and the index is an index into the
+ * LinearExtent table.
+ */
+#define LP_TARGET_TYPE_LINEAR 0
+
+/* This extent is a dm-zero target. The index is ignored and must be 0. */
+#define LP_TARGET_TYPE_ZERO 1
+
+/* This struct defines an extent entry in the extent table block. */
+typedef struct LpMetadataExtent {
+    /*  0: Length of this extent, in 512-byte sectors. */
+    uint64_t num_sectors;
+
+    /*  8: Target type for device-mapper (see LP_TARGET_TYPE_* values). */
+    uint32_t target_type;
+
+    /* 12: Contents depends on target_type.
+     *
+     * LINEAR: The sector on the physical partition that this extent maps onto.
+     * ZERO: This field must be 0.
+     */
+    uint64_t target_data;
+
+    /* 20: Contents depends on target_type.
+     *
+     * LINEAR: Must be an index into the block devices table.
+     * ZERO: This field must be 0.
+     */
+    uint32_t target_source;
+} __attribute__((packed)) LpMetadataExtent;
+
+/* This struct defines an entry in the groups table. Each group has a maximum
+ * size, and partitions in a group must not exceed that size. There is always
+ * a "default" group of unlimited size, which is used when not using update
+ * groups or when using overlayfs or fastbootd.
+ */
+typedef struct LpMetadataPartitionGroup {
+    /*  0: Name of this group. Any unused characters must be 0. */
+    char name[36];
+
+    /* 36: Flags (see LP_GROUP_*). */
+    uint32_t flags;
+
+    /* 40: Maximum size in bytes. If 0, the group has no maximum size. */
+    uint64_t maximum_size;
+} __attribute__((packed)) LpMetadataPartitionGroup;
+
+/* This flag is only intended to be used with super_empty.img and super.img on
+ * retrofit devices. If set, the group needs a slot suffix to be interpreted
+ * correctly. The suffix is automatically applied by ReadMetadata().
+ */
+#define LP_GROUP_SLOT_SUFFIXED (1 << 0)
+
+/* This struct defines an entry in the block_devices table. There must be at
+ * least one device, and the first device must represent the partition holding
+ * the super metadata.
+ */
+typedef struct LpMetadataBlockDevice {
+    /* 0: First usable sector for allocating logical partitions. this will be
+     * the first sector after the initial geometry blocks, followed by the
+     * space consumed by metadata_max_size*metadata_slot_count*2.
+     */
+    uint64_t first_logical_sector;
+
+    /* 8: Alignment for defining partitions or partition extents. For example,
+     * an alignment of 1MiB will require that all partitions have a size evenly
+     * divisible by 1MiB, and that the smallest unit the partition can grow by
+     * is 1MiB.
+     *
+     * Alignment is normally determined at runtime when growing or adding
+     * partitions. If for some reason the alignment cannot be determined, then
+     * this predefined alignment in the geometry is used instead. By default
+     * it is set to 1MiB.
+     */
+    uint32_t alignment;
+
+    /* 12: Alignment offset for "stacked" devices. For example, if the "super"
+     * partition itself is not aligned within the parent block device's
+     * partition table, then we adjust for this in deciding where to place
+     * |first_logical_sector|.
+     *
+     * Similar to |alignment|, this will be derived from the operating system.
+     * If it cannot be determined, it is assumed to be 0.
+     */
+    uint32_t alignment_offset;
+
+    /* 16: Block device size, as specified when the metadata was created. This
+     * can be used to verify the geometry against a target device.
+     */
+    uint64_t size;
+
+    /* 24: Partition name in the GPT. Any unused characters must be 0. */
+    char partition_name[36];
+
+    /* 60: Flags (see LP_BLOCK_DEVICE_* flags below). */
+    uint32_t flags;
+} __attribute__((packed)) LpMetadataBlockDevice;
+
+/* This flag is only intended to be used with super_empty.img and super.img on
+ * retrofit devices. On these devices there are A and B super partitions, and
+ * we don't know ahead of time which slot the image will be applied to.
+ *
+ * If set, the block device needs a slot suffix applied before being used with
+ * IPartitionOpener. The slot suffix is determined by the metadata slot number
+ * (0 = _a, 1 = _b).
+ */
+#define LP_BLOCK_DEVICE_SLOT_SUFFIXED (1 << 0)
+
+/* For ease of writing compatibility checks, the original metadata header is
+ * preserved below, and typedefs are provided for the current version.
+ */
+typedef struct LpMetadataHeaderV1_0 {
+    uint32_t magic;
+    uint16_t major_version;
+    uint16_t minor_version;
+    uint32_t header_size;
+    uint8_t header_checksum[32];
+    uint32_t tables_size;
+    uint8_t tables_checksum[32];
+    LpMetadataTableDescriptor partitions;
+    LpMetadataTableDescriptor extents;
+    LpMetadataTableDescriptor groups;
+    LpMetadataTableDescriptor block_devices;
+} __attribute__((packed)) LpMetadataHeaderV1_0;
+
+typedef LpMetadataHeader LpMetadataHeaderV1_2;
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* LOGICAL_PARTITION_METADATA_FORMAT_H_ */
diff --git a/src/dynamic-partitions/liblp/reader.cpp b/src/dynamic-partitions/liblp/reader.cpp
new file mode 100644 (file)
index 0000000..4f291be
--- /dev/null
@@ -0,0 +1,502 @@
+/*
+ * 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.
+ */
+
+#include "reader.h"
+
+#include <fcntl.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <functional>
+
+#include "utility.h"
+
+namespace android {
+namespace fs_mgr {
+
+static_assert(sizeof(LpMetadataHeaderV1_0) == offsetof(LpMetadataHeader, flags),
+              "Incorrect LpMetadataHeader v0 size");
+
+// Helper class for reading descriptors and memory buffers in the same manner.
+class Reader {
+  public:
+    virtual ~Reader(){};
+    virtual bool ReadFully(void* buffer, size_t length) = 0;
+};
+
+class FileReader final : public Reader {
+  public:
+    explicit FileReader(int fd) : fd_(fd) {}
+    bool ReadFully(void* buffer, size_t length) override {
+        return android::fs_mgr::ReadFully(fd_, buffer, length);
+    }
+
+  private:
+    int fd_;
+};
+
+class MemoryReader final : public Reader {
+  public:
+    MemoryReader(const void* buffer, size_t size)
+        : buffer_(reinterpret_cast<const uint8_t*>(buffer)), size_(size), pos_(0) {}
+    bool ReadFully(void* out, size_t length) override {
+        if (size_ - pos_ < length) {
+            errno = EINVAL;
+            return false;
+        }
+        memcpy(out, buffer_ + pos_, length);
+        pos_ += length;
+        return true;
+    }
+
+  private:
+    const uint8_t* buffer_;
+    size_t size_;
+    size_t pos_;
+};
+
+bool ParseGeometry(const void* buffer, LpMetadataGeometry* geometry) {
+    static_assert(sizeof(*geometry) <= LP_METADATA_GEOMETRY_SIZE);
+    memcpy(geometry, buffer, sizeof(*geometry));
+
+    // Check the magic signature.
+    if (geometry->magic != LP_METADATA_GEOMETRY_MAGIC) {
+        LERROR << "Logical partition metadata has invalid geometry magic signature.";
+        return false;
+    }
+    // Reject if the struct size is larger than what we compiled. This is so we
+    // can compute a checksum with the |struct_size| field rather than using
+    // sizeof.
+    if (geometry->struct_size > sizeof(LpMetadataGeometry)) {
+        LERROR << "Logical partition metadata has unrecognized fields.";
+        return false;
+    }
+    // Recompute and check the CRC32.
+    {
+        LpMetadataGeometry temp = *geometry;
+        memset(&temp.checksum, 0, sizeof(temp.checksum));
+        SHA256(&temp, temp.struct_size, temp.checksum);
+        if (memcmp(temp.checksum, geometry->checksum, sizeof(temp.checksum)) != 0) {
+            LERROR << "Logical partition metadata has invalid geometry checksum.";
+            return false;
+        }
+    }
+    // Check that the struct size is equal (this will have to change if we ever
+    // change the struct size in a release).
+    if (geometry->struct_size != sizeof(LpMetadataGeometry)) {
+        LERROR << "Logical partition metadata has invalid struct size.";
+        return false;
+    }
+    if (geometry->metadata_slot_count == 0) {
+        LERROR << "Logical partition metadata has invalid slot count.";
+        return false;
+    }
+    if (geometry->metadata_max_size % LP_SECTOR_SIZE != 0) {
+        LERROR << "Metadata max size is not sector-aligned.";
+        return false;
+    }
+    return true;
+}
+
+bool ReadPrimaryGeometry(int fd, LpMetadataGeometry* geometry) {
+    std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(LP_METADATA_GEOMETRY_SIZE);
+    if (SeekFile64(fd, GetPrimaryGeometryOffset(), SEEK_SET) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << " lseek failed";
+        return false;
+    }
+    if (!ReadFully(fd, buffer.get(), LP_METADATA_GEOMETRY_SIZE)) {
+        PERROR << __PRETTY_FUNCTION__ << " read " << LP_METADATA_GEOMETRY_SIZE << " bytes failed";
+        return false;
+    }
+    return ParseGeometry(buffer.get(), geometry);
+}
+
+bool ReadBackupGeometry(int fd, LpMetadataGeometry* geometry) {
+    std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(LP_METADATA_GEOMETRY_SIZE);
+    if (SeekFile64(fd, GetBackupGeometryOffset(), SEEK_SET) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << " lseek failed";
+        return false;
+    }
+    if (!ReadFully(fd, buffer.get(), LP_METADATA_GEOMETRY_SIZE)) {
+        PERROR << __PRETTY_FUNCTION__ << " backup read " << LP_METADATA_GEOMETRY_SIZE
+               << " bytes failed";
+        return false;
+    }
+    return ParseGeometry(buffer.get(), geometry);
+}
+
+// Read and validate geometry information from a block device that holds
+// logical partitions. If the information is corrupted, this will attempt
+// to read it from a secondary backup location.
+bool ReadLogicalPartitionGeometry(int fd, LpMetadataGeometry* geometry) {
+    if (ReadPrimaryGeometry(fd, geometry)) {
+        return true;
+    }
+    return ReadBackupGeometry(fd, geometry);
+}
+
+static bool ValidateTableBounds(const LpMetadataHeader& header,
+                                const LpMetadataTableDescriptor& table) {
+    if (table.offset > header.tables_size) {
+        return false;
+    }
+    uint64_t table_size = uint64_t(table.num_entries) * table.entry_size;
+    if (header.tables_size - table.offset < table_size) {
+        return false;
+    }
+    return true;
+}
+
+static bool ReadMetadataHeader(Reader* reader, LpMetadata* metadata) {
+    // Note we zero the struct since older files will result in a partial read.
+    LpMetadataHeader& header = metadata->header;
+    memset(&header, 0, sizeof(header));
+
+    if (!reader->ReadFully(&header, sizeof(LpMetadataHeaderV1_0))) {
+        PERROR << __PRETTY_FUNCTION__ << " read failed";
+        return false;
+    }
+
+    // Do basic validity checks before computing the checksum.
+    if (header.magic != LP_METADATA_HEADER_MAGIC) {
+        LERROR << "Logical partition metadata has invalid magic value.";
+        return false;
+    }
+    if (header.major_version != LP_METADATA_MAJOR_VERSION ||
+        header.minor_version > LP_METADATA_MINOR_VERSION_MAX) {
+        LERROR << "Logical partition metadata has incompatible version.";
+        return false;
+    }
+
+    // Validate the header struct size against the reported version.
+    uint32_t expected_struct_size = sizeof(header);
+    if (header.minor_version < LP_METADATA_VERSION_FOR_EXPANDED_HEADER) {
+        expected_struct_size = sizeof(LpMetadataHeaderV1_0);
+    }
+    if (header.header_size != expected_struct_size) {
+        LERROR << "Invalid partition metadata header struct size.";
+        return false;
+    }
+
+    // Read in any remaining fields, the last step needed before checksumming.
+    if (size_t remaining_bytes = header.header_size - sizeof(LpMetadataHeaderV1_0)) {
+        uint8_t* offset = reinterpret_cast<uint8_t*>(&header) + sizeof(LpMetadataHeaderV1_0);
+        if (!reader->ReadFully(offset, remaining_bytes)) {
+            PERROR << __PRETTY_FUNCTION__ << " read failed";
+            return false;
+        }
+    }
+
+    // To compute the header's checksum, we have to temporarily set its checksum
+    // field to 0. Note that we must only compute up to |header_size|.
+    {
+        LpMetadataHeader temp = header;
+        memset(&temp.header_checksum, 0, sizeof(temp.header_checksum));
+        SHA256(&temp, temp.header_size, temp.header_checksum);
+        if (memcmp(temp.header_checksum, header.header_checksum, sizeof(temp.header_checksum)) !=
+            0) {
+            LERROR << "Logical partition metadata has invalid checksum.";
+            return false;
+        }
+    }
+
+    if (!ValidateTableBounds(header, header.partitions) ||
+        !ValidateTableBounds(header, header.extents) ||
+        !ValidateTableBounds(header, header.groups) ||
+        !ValidateTableBounds(header, header.block_devices)) {
+        LERROR << "Logical partition metadata has invalid table bounds.";
+        return false;
+    }
+    // Check that table entry sizes can accomodate their respective structs. If
+    // table sizes change, these checks will have to be adjusted.
+    if (header.partitions.entry_size != sizeof(LpMetadataPartition)) {
+        LERROR << "Logical partition metadata has invalid partition table entry size.";
+        return false;
+    }
+    if (header.extents.entry_size != sizeof(LpMetadataExtent)) {
+        LERROR << "Logical partition metadata has invalid extent table entry size.";
+        return false;
+    }
+    if (header.groups.entry_size != sizeof(LpMetadataPartitionGroup)) {
+        LERROR << "Logical partition metadata has invalid group table entry size.";
+        return false;
+    }
+    return true;
+}
+
+// Parse and validate all metadata at the current position in the given file
+// descriptor.
+static std::unique_ptr<LpMetadata> ParseMetadata(const LpMetadataGeometry& geometry,
+                                                 Reader* reader) {
+    // First read and validate the header.
+    std::unique_ptr<LpMetadata> metadata = std::make_unique<LpMetadata>();
+
+    metadata->geometry = geometry;
+    if (!ReadMetadataHeader(reader, metadata.get())) {
+        return nullptr;
+    }
+
+    LpMetadataHeader& header = metadata->header;
+
+    // Check the table size.
+    if (header.tables_size > geometry.metadata_max_size) {
+        LERROR << "Invalid partition metadata header table size.";
+        return nullptr;
+    }
+
+    // Read the metadata payload. Allocation is fallible since the table size
+    // could be large.
+    std::unique_ptr<uint8_t[]> buffer(new (std::nothrow) uint8_t[header.tables_size]);
+    if (!buffer) {
+        LERROR << "Out of memory reading logical partition tables.";
+        return nullptr;
+    }
+    if (!reader->ReadFully(buffer.get(), header.tables_size)) {
+        PERROR << __PRETTY_FUNCTION__ << " read " << header.tables_size << "bytes failed";
+        return nullptr;
+    }
+
+    uint8_t checksum[32];
+    SHA256(buffer.get(), header.tables_size, checksum);
+    if (memcmp(checksum, header.tables_checksum, sizeof(checksum)) != 0) {
+        LERROR << "Logical partition metadata has invalid table checksum.";
+        return nullptr;
+    }
+
+    uint32_t valid_attributes = LP_PARTITION_ATTRIBUTE_MASK_V0;
+    if (metadata->header.minor_version >= LP_METADATA_VERSION_FOR_UPDATED_ATTR) {
+        valid_attributes |= LP_PARTITION_ATTRIBUTE_MASK_V1;
+    }
+
+    // ValidateTableSize ensured that |cursor| is valid for the number of
+    // entries in the table.
+    uint8_t* cursor = buffer.get() + header.partitions.offset;
+    for (size_t i = 0; i < header.partitions.num_entries; i++) {
+        LpMetadataPartition partition;
+        memcpy(&partition, cursor, sizeof(partition));
+        cursor += header.partitions.entry_size;
+
+        if (partition.attributes & ~valid_attributes) {
+            LERROR << "Logical partition has invalid attribute set.";
+            return nullptr;
+        }
+        if (partition.first_extent_index + partition.num_extents < partition.first_extent_index) {
+            LERROR << "Logical partition first_extent_index + num_extents overflowed.";
+            return nullptr;
+        }
+        if (partition.first_extent_index + partition.num_extents > header.extents.num_entries) {
+            LERROR << "Logical partition has invalid extent list.";
+            return nullptr;
+        }
+        if (partition.group_index >= header.groups.num_entries) {
+            LERROR << "Logical partition has invalid group index.";
+            return nullptr;
+        }
+
+        metadata->partitions.push_back(partition);
+    }
+
+    cursor = buffer.get() + header.extents.offset;
+    for (size_t i = 0; i < header.extents.num_entries; i++) {
+        LpMetadataExtent extent;
+        memcpy(&extent, cursor, sizeof(extent));
+        cursor += header.extents.entry_size;
+
+        if (extent.target_type == LP_TARGET_TYPE_LINEAR &&
+            extent.target_source >= header.block_devices.num_entries) {
+            LERROR << "Logical partition extent has invalid block device.";
+            return nullptr;
+        }
+
+        metadata->extents.push_back(extent);
+    }
+
+    cursor = buffer.get() + header.groups.offset;
+    for (size_t i = 0; i < header.groups.num_entries; i++) {
+        LpMetadataPartitionGroup group = {};
+        memcpy(&group, cursor, sizeof(group));
+        cursor += header.groups.entry_size;
+
+        metadata->groups.push_back(group);
+    }
+
+    cursor = buffer.get() + header.block_devices.offset;
+    for (size_t i = 0; i < header.block_devices.num_entries; i++) {
+        LpMetadataBlockDevice device = {};
+        memcpy(&device, cursor, sizeof(device));
+        cursor += header.block_devices.entry_size;
+
+        metadata->block_devices.push_back(device);
+    }
+
+    const LpMetadataBlockDevice* super_device = GetMetadataSuperBlockDevice(*metadata.get());
+    if (!super_device) {
+        LERROR << "Metadata does not specify a super device.";
+        return nullptr;
+    }
+
+    // Check that the metadata area and logical partition areas don't overlap.
+    uint64_t metadata_region =
+            GetTotalMetadataSize(geometry.metadata_max_size, geometry.metadata_slot_count);
+    if (metadata_region > super_device->first_logical_sector * LP_SECTOR_SIZE) {
+        LERROR << "Logical partition metadata overlaps with logical partition contents.";
+        return nullptr;
+    }
+    return metadata;
+}
+
+std::unique_ptr<LpMetadata> ParseMetadata(const LpMetadataGeometry& geometry, const void* buffer,
+                                          size_t size) {
+    MemoryReader reader(buffer, size);
+    return ParseMetadata(geometry, &reader);
+}
+
+std::unique_ptr<LpMetadata> ParseMetadata(const LpMetadataGeometry& geometry, int fd) {
+    FileReader reader(fd);
+    return ParseMetadata(geometry, &reader);
+}
+
+std::unique_ptr<LpMetadata> ReadPrimaryMetadata(int fd, const LpMetadataGeometry& geometry,
+                                                uint32_t slot_number) {
+    int64_t offset = GetPrimaryMetadataOffset(geometry, slot_number);
+    if (SeekFile64(fd, offset, SEEK_SET) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << " lseek failed: offset " << offset;
+        return nullptr;
+    }
+    return ParseMetadata(geometry, fd);
+}
+
+std::unique_ptr<LpMetadata> ReadBackupMetadata(int fd, const LpMetadataGeometry& geometry,
+                                               uint32_t slot_number) {
+    int64_t offset = GetBackupMetadataOffset(geometry, slot_number);
+    if (SeekFile64(fd, offset, SEEK_SET) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << " lseek failed: offset " << offset;
+        return nullptr;
+    }
+    return ParseMetadata(geometry, fd);
+}
+
+namespace {
+
+bool AdjustMetadataForSlot(LpMetadata* metadata, uint32_t slot_number) {
+    std::string slot_suffix = SlotSuffixForSlotNumber(slot_number);
+    for (auto& partition : metadata->partitions) {
+        if (!(partition.attributes & LP_PARTITION_ATTR_SLOT_SUFFIXED)) {
+            continue;
+        }
+        std::string partition_name = GetPartitionName(partition) + slot_suffix;
+        if (partition_name.size() > sizeof(partition.name)) {
+            LERROR << __PRETTY_FUNCTION__ << " partition name too long: " << partition_name;
+            return false;
+        }
+        strncpy(partition.name, partition_name.c_str(), sizeof(partition.name));
+        partition.attributes &= ~LP_PARTITION_ATTR_SLOT_SUFFIXED;
+    }
+    for (auto& block_device : metadata->block_devices) {
+        if (!(block_device.flags & LP_BLOCK_DEVICE_SLOT_SUFFIXED)) {
+            continue;
+        }
+        std::string partition_name = GetBlockDevicePartitionName(block_device) + slot_suffix;
+        if (!UpdateBlockDevicePartitionName(&block_device, partition_name)) {
+            LERROR << __PRETTY_FUNCTION__ << " partition name too long: " << partition_name;
+            return false;
+        }
+        block_device.flags &= ~LP_BLOCK_DEVICE_SLOT_SUFFIXED;
+    }
+    for (auto& group : metadata->groups) {
+        if (!(group.flags & LP_GROUP_SLOT_SUFFIXED)) {
+            continue;
+        }
+        std::string group_name = GetPartitionGroupName(group) + slot_suffix;
+        if (!UpdatePartitionGroupName(&group, group_name)) {
+            LERROR << __PRETTY_FUNCTION__ << " group name too long: " << group_name;
+            return false;
+        }
+        group.flags &= ~LP_GROUP_SLOT_SUFFIXED;
+    }
+    return true;
+}
+
+}  // namespace
+
+std::unique_ptr<LpMetadata> ReadMetadata(const std::string& super_partition, uint32_t slot_number) {
+    int fd = open(super_partition.c_str(), O_RDONLY);
+    if (fd < 0) {
+        PERROR << __PRETTY_FUNCTION__ << " open failed: " << super_partition;
+        return nullptr;
+    }
+
+    LpMetadataGeometry geometry;
+    if (!ReadLogicalPartitionGeometry(fd, &geometry)) {
+        close(fd);
+        return nullptr;
+    }
+    if (slot_number >= geometry.metadata_slot_count) {
+        LERROR << __PRETTY_FUNCTION__ << " invalid metadata slot number";
+        close(fd);
+        return nullptr;
+    }
+
+    std::vector<int64_t> offsets = {
+            GetPrimaryMetadataOffset(geometry, slot_number),
+            GetBackupMetadataOffset(geometry, slot_number),
+    };
+    std::unique_ptr<LpMetadata> metadata;
+
+    for (const auto& offset : offsets) {
+        if (SeekFile64(fd, offset, SEEK_SET) < 0) {
+            PERROR << __PRETTY_FUNCTION__ << " lseek failed, offset " << offset;
+            continue;
+        }
+        if ((metadata = ParseMetadata(geometry, fd)) != nullptr) {
+            break;
+        }
+    }
+    if (!metadata || !AdjustMetadataForSlot(metadata.get(), slot_number)) {
+        close(fd);
+        return nullptr;
+    }
+
+    close(fd);
+    return metadata;
+}
+
+static std::string NameFromFixedArray(const char* name, size_t buffer_size) {
+    // If the end of the buffer has a null character, it's safe to assume the
+    // buffer is null terminated. Otherwise, we cap the string to the input
+    // buffer size.
+    if (name[buffer_size - 1] == '\0') {
+        return std::string(name);
+    }
+    return std::string(name, buffer_size);
+}
+
+std::string GetPartitionName(const LpMetadataPartition& partition) {
+    return NameFromFixedArray(partition.name, sizeof(partition.name));
+}
+
+std::string GetPartitionGroupName(const LpMetadataPartitionGroup& group) {
+    return NameFromFixedArray(group.name, sizeof(group.name));
+}
+
+std::string GetBlockDevicePartitionName(const LpMetadataBlockDevice& block_device) {
+    return NameFromFixedArray(block_device.partition_name, sizeof(block_device.partition_name));
+}
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/src/dynamic-partitions/liblp/reader.h b/src/dynamic-partitions/liblp/reader.h
new file mode 100644 (file)
index 0000000..7a2490b
--- /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.
+ */
+
+#ifndef LIBLP_READER_H_
+#define LIBLP_READER_H_
+
+#include <stddef.h>
+
+#include <memory>
+
+#include <liblp/liblp.h>
+
+namespace android {
+namespace fs_mgr {
+
+// Parse an LpMetadataGeometry from a buffer. The buffer must be at least
+// LP_METADATA_GEOMETRY_SIZE bytes in size.
+bool ParseGeometry(const void* buffer, LpMetadataGeometry* geometry);
+
+// Helper functions for manually reading geometry and metadata.
+std::unique_ptr<LpMetadata> ParseMetadata(const LpMetadataGeometry& geometry, int fd);
+std::unique_ptr<LpMetadata> ParseMetadata(const LpMetadataGeometry& geometry, const void* buffer,
+                                          size_t size);
+bool ReadLogicalPartitionGeometry(int fd, LpMetadataGeometry* geometry);
+bool ReadPrimaryGeometry(int fd, LpMetadataGeometry* geometry);
+bool ReadBackupGeometry(int fd, LpMetadataGeometry* geometry);
+
+// These functions assume a valid geometry and slot number, and do not obey
+// auto-slot-suffixing. They are used for tests and for checking whether
+// the metadata is coherent across primary and backup copies.
+std::unique_ptr<LpMetadata> ReadPrimaryMetadata(int fd, const LpMetadataGeometry& geometry,
+                                                uint32_t slot_number);
+std::unique_ptr<LpMetadata> ReadBackupMetadata(int fd, const LpMetadataGeometry& geometry,
+                                               uint32_t slot_number);
+
+}  // namespace fs_mgr
+}  // namespace android
+
+#endif /* LIBLP_READER_H_ */
diff --git a/src/dynamic-partitions/liblp/utility.cpp b/src/dynamic-partitions/liblp/utility.cpp
new file mode 100644 (file)
index 0000000..7884010
--- /dev/null
@@ -0,0 +1,245 @@
+/*
+ * 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.
+ */
+
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#if defined(__linux__)
+#include <linux/fs.h>
+#include <sys/ioctl.h>
+#endif
+
+#include <cstring>
+#include <map>
+#include <string>
+#include <vector>
+
+#include <openssl/sha.h>
+#include <openssl/evp.h>
+
+#include "utility.h"
+
+namespace android {
+namespace fs_mgr {
+
+int64_t SeekFile64(int fd, int64_t offset, int whence) {
+    static_assert(sizeof(off_t) == sizeof(int64_t), "Need 64-bit lseek");
+    return lseek(fd, offset, whence);
+}
+
+int64_t GetPrimaryGeometryOffset() {
+    return LP_PARTITION_RESERVED_BYTES;
+}
+
+int64_t GetBackupGeometryOffset() {
+    return GetPrimaryGeometryOffset() + LP_METADATA_GEOMETRY_SIZE;
+}
+
+int64_t GetPrimaryMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number) {
+    CHECK(slot_number < geometry.metadata_slot_count);
+    int64_t offset = LP_PARTITION_RESERVED_BYTES + (LP_METADATA_GEOMETRY_SIZE * 2) +
+                     geometry.metadata_max_size * slot_number;
+    return offset;
+}
+
+int64_t GetBackupMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number) {
+    CHECK(slot_number < geometry.metadata_slot_count);
+    int64_t start = LP_PARTITION_RESERVED_BYTES + (LP_METADATA_GEOMETRY_SIZE * 2) +
+                    int64_t(geometry.metadata_max_size) * geometry.metadata_slot_count;
+    return start + int64_t(geometry.metadata_max_size * slot_number);
+}
+
+uint64_t GetTotalMetadataSize(uint32_t metadata_max_size, uint32_t max_slots) {
+    return LP_PARTITION_RESERVED_BYTES +
+           (LP_METADATA_GEOMETRY_SIZE + metadata_max_size * max_slots) * 2;
+}
+
+const LpMetadataBlockDevice* GetMetadataSuperBlockDevice(const LpMetadata& metadata) {
+    if (metadata.block_devices.empty()) {
+        return nullptr;
+    }
+    return &metadata.block_devices[0];
+}
+
+void SHA256(const void* data, size_t length, uint8_t out[32]) {
+    const EVP_MD *md = EVP_sha256();
+
+    if (!EVP_Digest(data, length, out, nullptr, md, nullptr))
+        LERROR << __PRETTY_FUNCTION__ << "Unable to compute hash";
+}
+
+uint32_t SlotNumberForSlotSuffix(const std::string& suffix) {
+    if (suffix.empty() || suffix == "a" || suffix == "_a") {
+        return 0;
+    } else if (suffix == "b" || suffix == "_b") {
+        return 1;
+    } else {
+        LERROR << __PRETTY_FUNCTION__ << "slot '" << suffix
+               << "' does not have a recognized format.";
+        return 0;
+    }
+}
+
+uint64_t GetTotalSuperPartitionSize(const LpMetadata& metadata) {
+    uint64_t size = 0;
+    for (const auto& block_device : metadata.block_devices) {
+        size += block_device.size;
+    }
+    return size;
+}
+
+std::vector<std::string> GetBlockDevicePartitionNames(const LpMetadata& metadata) {
+    std::vector<std::string> list;
+    for (const auto& block_device : metadata.block_devices) {
+        list.emplace_back(GetBlockDevicePartitionName(block_device));
+    }
+    return list;
+}
+
+const LpMetadataPartition* FindPartition(const LpMetadata& metadata, const std::string& name) {
+    for (const auto& partition : metadata.partitions) {
+        if (GetPartitionName(partition) == name) {
+            return &partition;
+        }
+    }
+    return nullptr;
+}
+
+uint64_t GetPartitionSize(const LpMetadata& metadata, const LpMetadataPartition& partition) {
+    uint64_t total_size = 0;
+    for (uint32_t i = 0; i < partition.num_extents; i++) {
+        const auto& extent = metadata.extents[partition.first_extent_index + i];
+        total_size += extent.num_sectors * LP_SECTOR_SIZE;
+    }
+    return total_size;
+}
+
+std::string GetPartitionSlotSuffix(const std::string& partition_name) {
+    if (partition_name.size() <= 2) {
+        return "";
+    }
+    std::string suffix = partition_name.substr(partition_name.size() - 2);
+    return (suffix == "_a" || suffix == "_b") ? suffix : "";
+}
+
+std::string SlotSuffixForSlotNumber(uint32_t slot_number) {
+    CHECK(slot_number == 0 || slot_number == 1);
+    return (slot_number == 0) ? "_a" : "_b";
+}
+
+bool UpdateBlockDevicePartitionName(LpMetadataBlockDevice* device, const std::string& name) {
+    if (name.size() > sizeof(device->partition_name)) {
+        return false;
+    }
+    strncpy(device->partition_name, name.c_str(), sizeof(device->partition_name));
+    return true;
+}
+
+bool UpdatePartitionGroupName(LpMetadataPartitionGroup* group, const std::string& name) {
+    if (name.size() > sizeof(group->name)) {
+        return false;
+    }
+    strncpy(group->name, name.c_str(), sizeof(group->name));
+    return true;
+}
+
+bool UpdatePartitionName(LpMetadataPartition* partition, const std::string& name) {
+    if (name.size() > sizeof(partition->name)) {
+        return false;
+    }
+    strncpy(partition->name, name.c_str(), sizeof(partition->name));
+    return true;
+}
+
+bool SetBlockReadonly(int fd, bool readonly) {
+#if defined(__linux__)
+    int val = readonly;
+    return ioctl(fd, BLKROSET, &val) == 0;
+#else
+    (void)fd;
+    (void)readonly;
+    return true;
+#endif
+}
+
+inline std::string ToHexString(uint64_t value) {
+    std::ostringstream stream;
+    stream << "0x" << std::hex << value;
+    return stream.str();
+}
+
+void SetMetadataHeaderV0(LpMetadata* metadata) {
+    if (metadata->header.minor_version <= LP_METADATA_MINOR_VERSION_MIN) {
+        return;
+    }
+    LINFO << "Forcefully setting metadata header version " << LP_METADATA_MAJOR_VERSION << "."
+          << metadata->header.minor_version << " to " << LP_METADATA_MAJOR_VERSION << "."
+          << LP_METADATA_MINOR_VERSION_MIN;
+    metadata->header.minor_version = LP_METADATA_MINOR_VERSION_MIN;
+    metadata->header.header_size = sizeof(LpMetadataHeaderV1_0);
+
+    // Retrofit Virtual A/B devices should have version 10.1, so flags shouldn't be set.
+    // Warn if this is the case, but zero it out anyways.
+    if (metadata->header.flags) {
+        LWARN << "Zeroing unexpected flags: " << ToHexString(metadata->header.flags);
+    }
+
+    // Zero out all fields beyond LpMetadataHeaderV0.
+    static_assert(sizeof(metadata->header) > sizeof(LpMetadataHeaderV1_0));
+    memset(reinterpret_cast<uint8_t*>(&metadata->header) + sizeof(LpMetadataHeaderV1_0), 0,
+           sizeof(metadata->header) - sizeof(LpMetadataHeaderV1_0));
+
+    // Clear partition attributes unknown to V0.
+    // On retrofit Virtual A/B devices, UPDATED flag may be set, so only log info here.
+    for (auto& partition : metadata->partitions) {
+        if (partition.attributes & ~LP_PARTITION_ATTRIBUTE_MASK_V0) {
+            LINFO << "Clearing " << GetPartitionName(partition)
+                  << " partition attribute: " << ToHexString(partition.attributes);
+        }
+
+        partition.attributes &= LP_PARTITION_ATTRIBUTE_MASK_V0;
+    }
+}
+
+bool ReadFully(int fd, void* data, size_t byte_count) {
+  uint8_t* p = reinterpret_cast<uint8_t*>(data);
+  size_t remaining = byte_count;
+  while (remaining > 0) {
+    ssize_t n = TEMP_FAILURE_RETRY(read(fd, p, remaining));
+    if (n <= 0) return false;
+    p += n;
+    remaining -= n;
+  }
+  return true;
+}
+
+bool WriteFully(int fd, void* data, size_t byte_count) {
+  uint8_t* p = reinterpret_cast<uint8_t*>(data);
+  size_t remaining = byte_count;
+  while (remaining > 0) {
+    ssize_t n = TEMP_FAILURE_RETRY(write(fd, p, remaining));
+    if (n <= 0) return false;
+    p += n;
+    remaining -= n;
+  }
+  return true;
+}
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/src/dynamic-partitions/liblp/utility.h b/src/dynamic-partitions/liblp/utility.h
new file mode 100644 (file)
index 0000000..209fc24
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * 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.
+ */
+
+#ifndef LIBLP_UTILITY_H
+#define LIBLP_UTILITY_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <cassert>
+#include <iostream>
+#include <limits>
+#include <sstream>
+#include <string>
+#include <string_view>
+
+#include <liblp/liblp.h>
+
+#define LP_TAG "[liblp]"
+#define LWARN NewlineLogger(std::cerr).stream << "[W]" << LP_TAG
+#define LINFO NewlineLogger(std::cerr).stream << "[I]" << LP_TAG
+#define LERROR NewlineLogger(std::cerr).stream << "[E]" << LP_TAG
+#define PWARNING LWARN
+#define PERROR LERROR
+
+#define CHECK assert
+
+namespace android {
+namespace fs_mgr {
+
+// Return the offset of the primary or backup geometry.
+int64_t GetPrimaryGeometryOffset();
+int64_t GetBackupGeometryOffset();
+
+// Return the offset of a primary metadata slot, relative to the start of the
+// device.
+int64_t GetPrimaryMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number);
+
+// Return the offset of a backup metadata slot, relative to the end of the
+// device.
+int64_t GetBackupMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number);
+
+// Return the total space at the start of the super partition that must be set
+// aside from headers/metadata and backups.
+uint64_t GetTotalMetadataSize(uint32_t metadata_max_size, uint32_t max_slots);
+
+// Cross-platform helper for lseek64().
+int64_t SeekFile64(int fd, int64_t offset, int whence);
+
+// Compute a SHA256 hash.
+void SHA256(const void* data, size_t length, uint8_t out[32]);
+
+// Align |base| such that it is evenly divisible by |alignment|, which does not
+// have to be a power of two. Return false on overflow.
+template <typename T>
+bool AlignTo(T base, uint32_t alignment, T* out) {
+    static_assert(std::numeric_limits<T>::is_integer);
+    static_assert(!std::numeric_limits<T>::is_signed);
+    if (!alignment) {
+        *out = base;
+        return true;
+    }
+    T remainder = base % alignment;
+    if (remainder == 0) {
+        *out = base;
+        return true;
+    }
+    T to_add = alignment - remainder;
+    if (to_add > std::numeric_limits<T>::max() - base) {
+        return false;
+    }
+    *out = base + to_add;
+    return true;
+}
+
+// Update names from C++ strings.
+bool UpdateBlockDevicePartitionName(LpMetadataBlockDevice* device, const std::string& name);
+bool UpdatePartitionGroupName(LpMetadataPartitionGroup* group, const std::string& name);
+bool UpdatePartitionName(LpMetadataPartition* partition, const std::string& name);
+
+// Call BLKROSET ioctl on fd so that fd is readonly / read-writable.
+bool SetBlockReadonly(int fd, bool readonly);
+
+// Forcefully set metadata header version to 1.0, clearing any incompatible flags and attributes
+// so that when downgrading to a build with liblp V0, the device still boots.
+void SetMetadataHeaderV0(LpMetadata* metadata);
+
+bool ReadFully(int fd, void* data, size_t byte_count);
+
+bool WriteFully(int fd, void* data, size_t byte_count);
+
+class NewlineLogger {
+  public:
+    NewlineLogger(std::ostream& sink) : sink_(sink) {}
+    ~NewlineLogger() {
+        sink_ << stream.str() << std::endl;
+    }
+
+    std::ostringstream stream;
+
+  private:
+    std::ostream& sink_;
+};
+
+}  // namespace fs_mgr
+}  // namespace android
+
+#endif  // LIBLP_UTILITY_H
diff --git a/src/dynamic-partitions/liblp/writer.cpp b/src/dynamic-partitions/liblp/writer.cpp
new file mode 100644 (file)
index 0000000..564a294
--- /dev/null
@@ -0,0 +1,415 @@
+/*
+ * Copyright (C) 2007 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.
+ */
+
+#include "writer.h"
+
+#include <fcntl.h>
+#include <inttypes.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <string>
+
+#include "reader.h"
+#include "utility.h"
+
+namespace android {
+namespace fs_mgr {
+
+std::string SerializeGeometry(const LpMetadataGeometry& input) {
+    LpMetadataGeometry geometry = input;
+    memset(geometry.checksum, 0, sizeof(geometry.checksum));
+    SHA256(&geometry, sizeof(geometry), geometry.checksum);
+
+    std::string blob(reinterpret_cast<const char*>(&geometry), sizeof(geometry));
+    blob.resize(LP_METADATA_GEOMETRY_SIZE);
+    return blob;
+}
+
+static bool CompareGeometry(const LpMetadataGeometry& g1, const LpMetadataGeometry& g2) {
+    return g1.metadata_max_size == g2.metadata_max_size &&
+           g1.metadata_slot_count == g2.metadata_slot_count &&
+           g1.logical_block_size == g2.logical_block_size;
+}
+
+std::string SerializeMetadata(const LpMetadata& input) {
+    LpMetadata metadata = input;
+    LpMetadataHeader& header = metadata.header;
+
+    // Serialize individual tables.
+    std::string partitions(reinterpret_cast<const char*>(metadata.partitions.data()),
+                           metadata.partitions.size() * sizeof(LpMetadataPartition));
+    std::string extents(reinterpret_cast<const char*>(metadata.extents.data()),
+                        metadata.extents.size() * sizeof(LpMetadataExtent));
+    std::string groups(reinterpret_cast<const char*>(metadata.groups.data()),
+                       metadata.groups.size() * sizeof(LpMetadataPartitionGroup));
+    std::string block_devices(reinterpret_cast<const char*>(metadata.block_devices.data()),
+                              metadata.block_devices.size() * sizeof(LpMetadataBlockDevice));
+
+    // Compute positions of tables.
+    header.partitions.offset = 0;
+    header.extents.offset = header.partitions.offset + partitions.size();
+    header.groups.offset = header.extents.offset + extents.size();
+    header.block_devices.offset = header.groups.offset + groups.size();
+    header.tables_size = header.block_devices.offset + block_devices.size();
+
+    // Compute payload checksum.
+    std::string tables = partitions + extents + groups + block_devices;
+    SHA256(tables.data(), tables.size(), header.tables_checksum);
+
+    // Compute header checksum.
+    memset(header.header_checksum, 0, sizeof(header.header_checksum));
+    SHA256(&header, header.header_size, header.header_checksum);
+
+    std::string header_blob =
+            std::string(reinterpret_cast<const char*>(&header), header.header_size);
+    return header_blob + tables;
+}
+
+// Perform checks so we don't accidentally overwrite valid metadata with
+// potentially invalid metadata, or random partition data with metadata.
+static bool ValidateAndSerializeMetadata(const LpMetadata& metadata, const std::string& slot_suffix,
+                                         std::string* blob) {
+    const LpMetadataGeometry& geometry = metadata.geometry;
+
+    *blob = SerializeMetadata(metadata);
+
+    // Make sure we're writing within the space reserved.
+    if (blob->size() > geometry.metadata_max_size) {
+        LERROR << "Logical partition metadata is too large. " << blob->size() << " > "
+               << geometry.metadata_max_size;
+        return false;
+    }
+
+    // Make sure the device has enough space to store two backup copies of the
+    // metadata.
+    uint64_t reserved_size = LP_METADATA_GEOMETRY_SIZE +
+                             uint64_t(geometry.metadata_max_size) * geometry.metadata_slot_count;
+    uint64_t total_reserved = LP_PARTITION_RESERVED_BYTES + reserved_size * 2;
+
+    const LpMetadataBlockDevice* super_device = GetMetadataSuperBlockDevice(metadata);
+    if (!super_device) {
+        LERROR << "Logical partition metadata does not have a super block device.";
+        return false;
+    }
+
+    if (total_reserved > super_device->first_logical_sector * LP_SECTOR_SIZE) {
+        LERROR << "Not enough space to store all logical partition metadata slots.";
+        return false;
+    }
+    for (const auto& block_device : metadata.block_devices) {
+        std::string partition_name = GetBlockDevicePartitionName(block_device);
+        if (block_device.flags & LP_BLOCK_DEVICE_SLOT_SUFFIXED) {
+            if (slot_suffix.empty()) {
+                LERROR << "Block device " << partition_name << " requires a slot suffix,"
+                       << " which could not be derived from the super partition name.";
+                return false;
+            }
+            partition_name += slot_suffix;
+        }
+
+        if ((block_device.first_logical_sector + 1) * LP_SECTOR_SIZE > block_device.size) {
+            LERROR << "Block device " << partition_name << " has invalid first sector "
+                   << block_device.first_logical_sector << " for size " << block_device.size;
+            return false;
+        }
+
+        // When flashing on the device, check partition sizes. Don't do this on
+        // the host since there is no way to verify.
+#if defined(__ANDROID__)
+        BlockDeviceInfo info;
+        if (!opener.GetInfo(partition_name, &info)) {
+            PERROR << partition_name << ": ioctl";
+            return false;
+        }
+        if (info.size != block_device.size) {
+            LERROR << "Block device " << partition_name << " size mismatch (expected"
+                   << block_device.size << ", got " << info.size << ")";
+            return false;
+        }
+#endif
+    }
+
+    // Make sure all partition entries reference valid extents.
+    for (const auto& partition : metadata.partitions) {
+        if (partition.first_extent_index + partition.num_extents > metadata.extents.size()) {
+            LERROR << "Partition references invalid extent.";
+            return false;
+        }
+    }
+
+    // Make sure all linear extents have a valid range.
+    uint64_t last_sector = super_device->size / LP_SECTOR_SIZE;
+    for (const auto& extent : metadata.extents) {
+        if (extent.target_type == LP_TARGET_TYPE_LINEAR) {
+            uint64_t physical_sector = extent.target_data;
+            if (physical_sector < super_device->first_logical_sector ||
+                physical_sector + extent.num_sectors > last_sector) {
+                LERROR << "Extent table entry is out of bounds.";
+                return false;
+            }
+        }
+    }
+    return true;
+}
+
+// Check that the given region is within metadata bounds.
+static bool ValidateMetadataRegion(const LpMetadata& metadata, uint64_t start, size_t size) {
+    const LpMetadataBlockDevice* super_device = GetMetadataSuperBlockDevice(metadata);
+    if (!super_device) {
+        LERROR << __PRETTY_FUNCTION__ << " could not locate super block device in metadata";
+        return false;
+    }
+    if (start + size >= super_device->first_logical_sector * LP_SECTOR_SIZE) {
+        LERROR << __PRETTY_FUNCTION__ << " write of " << size << " bytes at " << start
+               << " overlaps with logical partition contents";
+        return false;
+    }
+    return true;
+}
+
+static bool WritePrimaryMetadata(int fd, const LpMetadata& metadata, uint32_t slot_number,
+                                 std::string& blob,
+                                 const std::function<bool(int, std::string&)>& writer) {
+    int64_t primary_offset = GetPrimaryMetadataOffset(metadata.geometry, slot_number);
+    if (!ValidateMetadataRegion(metadata, primary_offset, blob.size())) {
+        return false;
+    }
+    if (SeekFile64(fd, primary_offset, SEEK_SET) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << " lseek failed: offset " << primary_offset;
+        return false;
+    }
+    if (!writer(fd, blob)) {
+        PERROR << __PRETTY_FUNCTION__ << " write " << blob.size() << " bytes failed";
+        return false;
+    }
+    return true;
+}
+
+static bool WriteBackupMetadata(int fd, const LpMetadata& metadata, uint32_t slot_number,
+                                std::string& blob,
+                                const std::function<bool(int, std::string&)>& writer) {
+    int64_t backup_offset = GetBackupMetadataOffset(metadata.geometry, slot_number);
+    if (!ValidateMetadataRegion(metadata, backup_offset, blob.size())) {
+        return false;
+    }
+    if (SeekFile64(fd, backup_offset, SEEK_SET) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << " lseek failed: offset " << backup_offset;
+        return false;
+    }
+    if (!writer(fd, blob)) {
+        PERROR << __PRETTY_FUNCTION__ << " backup write " << blob.size() << " bytes failed";
+        return false;
+    }
+    return true;
+}
+
+static bool WriteMetadata(int fd, const LpMetadata& metadata, uint32_t slot_number,
+                          std::string& blob,
+                          const std::function<bool(int, std::string&)>& writer) {
+    // Make sure we're writing to a valid metadata slot.
+    if (slot_number >= metadata.geometry.metadata_slot_count) {
+        LERROR << "Invalid logical partition metadata slot number.";
+        return false;
+    }
+    if (!WritePrimaryMetadata(fd, metadata, slot_number, blob, writer)) {
+        return false;
+    }
+    if (!WriteBackupMetadata(fd, metadata, slot_number, blob, writer)) {
+        return false;
+    }
+    return true;
+}
+
+static bool DefaultWriter(int fd, std::string& blob) {
+    return android::fs_mgr::WriteFully(fd, blob.data(), blob.size());
+}
+
+#if defined(_WIN32)
+static const int O_SYNC = 0;
+#endif
+
+bool FlashPartitionTable(const std::string& super_partition,
+                         const LpMetadata& metadata) {
+    int fd = open(super_partition.c_str(), O_RDWR | O_SYNC);
+    if (fd < 0) {
+        PERROR << __PRETTY_FUNCTION__ << " open failed: " << super_partition;
+        return false;
+    }
+
+    // This is only used in update_engine and fastbootd, where the super
+    // partition should be specified as a name (or by-name link), and
+    // therefore, we should be able to extract a slot suffix.
+    std::string slot_suffix = GetPartitionSlotSuffix(super_partition);
+
+    // Before writing geometry and/or logical partition tables, perform some
+    // basic checks that the geometry and tables are coherent, and will fit
+    // on the given block device.
+    std::string metadata_blob;
+    if (!ValidateAndSerializeMetadata(metadata, slot_suffix, &metadata_blob)) {
+        close(fd);
+        return false;
+    }
+
+    // On retrofit devices, super_partition is system_other and might be set to readonly by
+    // fs_mgr_set_blk_ro(). Unset readonly so that fd can be written to.
+    if (!SetBlockReadonly(fd, false)) {
+        PWARNING << __PRETTY_FUNCTION__ << " BLKROSET 0 failed: " << super_partition;
+    }
+
+    // Write zeroes to the first block.
+    std::string zeroes(LP_PARTITION_RESERVED_BYTES, 0);
+    if (SeekFile64(fd, 0, SEEK_SET) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << " lseek failed: offset 0";
+        close(fd);
+        return false;
+    }
+    if (!android::fs_mgr::WriteFully(fd, zeroes.data(), zeroes.size())) {
+        PERROR << __PRETTY_FUNCTION__ << " write " << zeroes.size() << " bytes failed";
+        close(fd);
+        return false;
+    }
+
+    LWARN << "Flashing new logical partition geometry to " << super_partition;
+
+    // Write geometry to the primary and backup locations.
+    std::string blob = SerializeGeometry(metadata.geometry);
+    if (SeekFile64(fd, GetPrimaryGeometryOffset(), SEEK_SET) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << " lseek failed: primary geometry";
+        close(fd);
+        return false;
+    }
+    if (!android::fs_mgr::WriteFully(fd, blob.data(), blob.size())) {
+        PERROR << __PRETTY_FUNCTION__ << " write " << blob.size() << " bytes failed";
+        close(fd);
+        return false;
+    }
+    if (SeekFile64(fd, GetBackupGeometryOffset(), SEEK_SET) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << " lseek failed: backup geometry";
+        close(fd);
+        return false;
+    }
+    if (!android::fs_mgr::WriteFully(fd, blob.data(), blob.size())) {
+        PERROR << __PRETTY_FUNCTION__ << " backup write " << blob.size() << " bytes failed";
+        close(fd);
+        return false;
+    }
+
+    bool ok = true;
+    for (size_t i = 0; i < metadata.geometry.metadata_slot_count; i++) {
+        ok &= WriteMetadata(fd, metadata, i, metadata_blob, DefaultWriter);
+    }
+
+    close(fd);
+    return ok;
+}
+
+static bool CompareMetadata(const LpMetadata& a, const LpMetadata& b) {
+    return !memcmp(a.header.header_checksum, b.header.header_checksum,
+                   sizeof(a.header.header_checksum));
+}
+
+bool UpdatePartitionTable(const std::string& super_partition,
+                          const LpMetadata& metadata, uint32_t slot_number) {
+    int fd = open(super_partition.c_str(), O_RDWR | O_SYNC);
+    if (fd < 0) {
+        PERROR << __PRETTY_FUNCTION__ << " open failed: " << super_partition;
+        return false;
+    }
+
+    std::string slot_suffix = SlotSuffixForSlotNumber(slot_number);
+
+    // Before writing geometry and/or logical partition tables, perform some
+    // basic checks that the geometry and tables are coherent, and will fit
+    // on the given block device.
+    std::string blob;
+    if (!ValidateAndSerializeMetadata(metadata, slot_suffix, &blob)) {
+        close(fd);
+        return false;
+    }
+
+    // Verify that the old geometry is identical. If it's not, then we might be
+    // writing a table that was built for a different device, so we must reject
+    // it.
+    const LpMetadataGeometry& geometry = metadata.geometry;
+    LpMetadataGeometry old_geometry;
+    if (!ReadLogicalPartitionGeometry(fd, &old_geometry)) {
+        close(fd);
+        return false;
+    }
+    if (!CompareGeometry(geometry, old_geometry)) {
+        LERROR << "Incompatible geometry in new logical partition metadata";
+        close(fd);
+        return false;
+    }
+
+    // Validate the slot number now, before we call Read*Metadata.
+    if (slot_number >= geometry.metadata_slot_count) {
+        LERROR << "Invalid logical partition metadata slot number.";
+        close(fd);
+        return false;
+    }
+
+    // Try to read both existing copies of the metadata, if any.
+    std::unique_ptr<LpMetadata> primary = ReadPrimaryMetadata(fd, geometry, slot_number);
+    std::unique_ptr<LpMetadata> backup = ReadBackupMetadata(fd, geometry, slot_number);
+
+    if (primary && (!backup || !CompareMetadata(*primary.get(), *backup.get()))) {
+        // If the backup copy does not match the primary copy, we first
+        // synchronize the backup copy. This guarantees that a partial write
+        // still leaves one copy intact.
+        std::string old_blob;
+        if (!ValidateAndSerializeMetadata(*primary.get(), slot_suffix, &old_blob)) {
+            LERROR << "Error serializing primary metadata to repair corrupted backup";
+            close(fd);
+            return false;
+        }
+        if (!WriteBackupMetadata(fd, metadata, slot_number, old_blob, DefaultWriter)) {
+            LERROR << "Error writing primary metadata to repair corrupted backup";
+            close(fd);
+            return false;
+        }
+    } else if (backup && !primary) {
+        // The backup copy is coherent, and the primary is not. Sync it for
+        // safety.
+        std::string old_blob;
+        if (!ValidateAndSerializeMetadata(*backup.get(), slot_suffix, &old_blob)) {
+            LERROR << "Error serializing backup metadata to repair corrupted primary";
+            close(fd);
+            return false;
+        }
+        if (!WritePrimaryMetadata(fd, metadata, slot_number, old_blob, DefaultWriter)) {
+            LERROR << "Error writing backup metadata to repair corrupted primary";
+            close(fd);
+            return false;
+        }
+    }
+
+    // Both copies should now be in sync, so we can continue the update.
+    if (!WriteMetadata(fd, metadata, slot_number, blob, DefaultWriter)) {
+        close(fd);
+        return false;
+    }
+
+    LINFO << "Updated logical partition table at slot " << slot_number << " on device "
+          << super_partition;
+
+    close(fd);
+    return true;
+}
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/src/dynamic-partitions/liblp/writer.h b/src/dynamic-partitions/liblp/writer.h
new file mode 100644 (file)
index 0000000..ffad441
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+#ifndef LIBLP_WRITER_H
+#define LIBLP_WRITER_H
+
+#include <functional>
+#include <string>
+
+#include <liblp/liblp.h>
+
+namespace android {
+namespace fs_mgr {
+
+std::string SerializeGeometry(const LpMetadataGeometry& input);
+std::string SerializeMetadata(const LpMetadata& input);
+
+}  // namespace fs_mgr
+}  // namespace android
+
+#endif /* LIBLP_WRITER_H */
diff --git a/src/dynamic-partitions/parse-dynparts/.clang-format b/src/dynamic-partitions/parse-dynparts/.clang-format
new file mode 100644 (file)
index 0000000..06e3c51
--- /dev/null
@@ -0,0 +1,4 @@
+---
+Language: Cpp
+BasedOnStyle: Google
+...
diff --git a/src/dynamic-partitions/parse-dynparts/.gitignore b/src/dynamic-partitions/parse-dynparts/.gitignore
new file mode 100644 (file)
index 0000000..796b96d
--- /dev/null
@@ -0,0 +1 @@
+/build
diff --git a/src/dynamic-partitions/parse-dynparts/.vscode/extensions.json b/src/dynamic-partitions/parse-dynparts/.vscode/extensions.json
new file mode 100644 (file)
index 0000000..a6a5990
--- /dev/null
@@ -0,0 +1,12 @@
+{
+       // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
+       // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
+       // List of extensions which should be recommended for users of this workspace.
+       "recommendations": [
+               "ms-vscode.cpptools",
+               "ms-vscode.cmake-tools",
+               "twxs.cmake",
+       ],
+       // List of extensions recommended by VS Code that should not be recommended for users of this workspace.
+       "unwantedRecommendations": []
+}
diff --git a/src/dynamic-partitions/parse-dynparts/.vscode/settings.json b/src/dynamic-partitions/parse-dynparts/.vscode/settings.json
new file mode 100644 (file)
index 0000000..add6fba
--- /dev/null
@@ -0,0 +1,4 @@
+{
+       "C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools",
+       "cmake.configureOnOpen": true
+}
diff --git a/src/dynamic-partitions/parse-dynparts/CMakeLists.txt b/src/dynamic-partitions/parse-dynparts/CMakeLists.txt
new file mode 100644 (file)
index 0000000..3fe9f2c
--- /dev/null
@@ -0,0 +1,15 @@
+set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD_REQUIRED True)
+
+add_executable(parse-dynparts main.cpp lib.cpp)
+target_compile_options(parse-dynparts PRIVATE -Wall -Wextra -pedantic -fPIE)
+
+target_link_libraries(parse-dynparts PRIVATE lp)
+install(TARGETS parse-dynparts DESTINATION ${INSTALL_DIR})
+
+add_executable(parse-dynparts-test test/test.cpp lib.cpp ../testlib/metadataio.h ../testlib/metadataio.cpp)
+target_compile_options(parse-dynparts-test PRIVATE -Wall -Wextra -pedantic -fPIE -Wno-stringop-truncation)
+target_link_libraries(parse-dynparts-test PRIVATE GTest::gtest_main lp)
+
+include(GoogleTest)
+gtest_discover_tests(parse-dynparts-test)
diff --git a/src/dynamic-partitions/parse-dynparts/LICENSE b/src/dynamic-partitions/parse-dynparts/LICENSE
new file mode 100644 (file)
index 0000000..261eeb9
--- /dev/null
@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   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.
diff --git a/src/dynamic-partitions/parse-dynparts/README.md b/src/dynamic-partitions/parse-dynparts/README.md
new file mode 100644 (file)
index 0000000..848b5c9
--- /dev/null
@@ -0,0 +1,88 @@
+Purpose
+=======
+Most devices running Android 10 and higher use Android's [Dynamic Partitions][1]
+feature to allow the different read-only system partitions (e.g. `system`,
+`vendor`, `product`) to share the same pool of storage space. This allows
+vendors to safely resize those partitions in OTA updates, as long as the sum of
+their sizes doesn't exceed that of the physical partition they all reside in.
+
+The physical partition image that holds multiple Android dynamic partitions is
+conventionally named `super.img` and holds similar information as an LVM
+physical volume on Linux: a list of logical partitions, each associated with a
+(possibly non-contiguous) set of blocks in the file that comprise it. Like LVM,
+Android makes use of [Device Mapper's dm-linear target][2] to inform the
+kernel of the logical partitions so it can map them to block devices in
+`/dev/mapper`.
+
+In true Google fashion, however, Android dynamic partitions use a totally custom
+header format that is not compatible with LVM or other similar software. As
+such, the only official tools that exist to mount them are part of Android and
+depend heavily on Android's frameworks, volume manager, and init system. (There
+are [official tools][3] that run on Linux to pack and unpack `super.img` files,
+but they cannot mount them in-place.)
+
+This tool makes it possible to mount `super.img` files with a standard Linux
+userspace. It uses a modified version of Google's AOSP code to parse the
+partition layout, then outputs that layout as a textual "concise device
+specification" which, when passed to `dmsetup`, instructs the kernel to create
+a Device Mapper block device for each logical partition in the image.
+
+[1]: https://source.android.com/devices/tech/ota/dynamic_partitions
+[2]: https://www.kernel.org/doc/html/latest/admin-guide/device-mapper/linear.html
+[3]: https://android.googlesource.com/platform/system/extras/+/master/partition_tools/
+
+Dependencies
+============
+ - CMake
+ - OpenSSL (for hash functions)
+
+Building
+========
+This is a standard C++ CMake project; it builds like any other CMake project.
+For those unfamiliar with CMake, here's the incantation you need to build using
+[Ninja](https://ninja-build.org/) as a backend:
+```
+mkdir build
+cd build
+cmake -G Ninja ..
+ninja
+```
+
+Or, if you don't have Ninja, you can use the Makefile backend:
+```
+mkdir build
+cd build
+cmake ..
+make
+```
+
+Usage
+=====
+
+Setup
+-----
+ 1. Obtain a raw `super.img`. Depending on the source of the image you're
+    working with, this may initially be a sparse image, which you'll have to
+    unsparse using the standard Android `simg2img` tool, or it may be one
+    partition inside a GPT-partitioned disk image.
+ 2. Make your `super.img` available as a loop device (omit `-r` if you want to
+    allow writes):
+    ```
+    losetup -r /dev/loop0 super.img
+    ```
+ 3. Create mappings for the dynamic partitions:
+    ```
+    dmsetup create --concise "$(parse-dynparts /dev/loop0)"
+    ```
+ 4. Access your partitions as `/dev/mapper/dynpart-<NAME>`!
+
+Teardown
+--------
+ 1. Unmap the Device Mapper devices:
+    ```
+    dmsetup remove /dev/mapper/dynpart-*
+    ```
+ 2. Delete the loop device:
+    ```
+    losetup -d /dev/loop0
+    ```
diff --git a/src/dynamic-partitions/parse-dynparts/lib.cpp b/src/dynamic-partitions/parse-dynparts/lib.cpp
new file mode 100644 (file)
index 0000000..1981b05
--- /dev/null
@@ -0,0 +1,74 @@
+/* Copyright (c) 2021 Tom Hebb (https://github.com/tchebb/parse-android-dynparts)
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * SPDX-License-Identifier: Apache-2.0 */
+
+#include "lib.hpp"
+
+#include <iostream>
+#include <sstream>
+#include <string_view>
+
+using namespace android::fs_mgr;
+using std::endl;
+
+std::optional<std::string> go(const android::fs_mgr::LpMetadata &metadata, bool list_tables, std::string_view device_name, std::ostream &messages) {
+  std::string table;
+
+  // Code structure taken from Android's system/core/fs_mgr/fs_mgr_dm_linear.cpp
+  for (const auto& partition : metadata.partitions) {
+    if (!partition.num_extents) {
+      messages << "Skipping zero-length logical partition: "
+               << GetPartitionName(partition) << endl;
+      continue;
+    }
+    if (partition.attributes & LP_PARTITION_ATTR_DISABLED) {
+      messages << "Skipping disabled partition: " << GetPartitionName(partition)
+               << endl;
+      continue;
+    }
+
+    std::ostringstream line;
+
+    if (list_tables)
+      line << GetPartitionName(partition) << " ";
+    else {
+      bool read_only = partition.attributes & LP_PARTITION_ATTR_READONLY;
+      line << GetPartitionName(partition) << ",,," << (read_only ? "ro," : "rw,");
+    }
+
+    uint64_t sector = 0;
+    for (size_t i = 0; i < partition.num_extents; i++) {
+      const auto& extent = metadata.extents[partition.first_extent_index + i];
+      switch (extent.target_type) {
+        case LP_TARGET_TYPE_ZERO:
+          line << sector << " " << extent.num_sectors << " zero";
+          break;
+        case LP_TARGET_TYPE_LINEAR: {
+          if (extent.target_source != 0) {
+            messages << "This utility does not yet support multiple block devices"
+                     << endl;
+            return std::nullopt;
+          }
+
+          if (i && !list_tables) line << ",";
+          if (i && list_tables) line << "\\n";
+
+          line << sector << " " << extent.num_sectors << " linear "
+               << device_name << " " << extent.target_data;
+          break;
+        }
+        default:
+          messages << "Unknown target type in metadata: " << extent.target_type
+                << endl;
+          return std::nullopt;
+      }
+      sector += extent.num_sectors;
+    }
+
+    if (!table.empty() && list_tables) table += "\n";
+    if (!table.empty() && !list_tables) table += ";";
+    table += line.str();
+  }
+
+  return table;
+}
\ No newline at end of file
diff --git a/src/dynamic-partitions/parse-dynparts/lib.hpp b/src/dynamic-partitions/parse-dynparts/lib.hpp
new file mode 100644 (file)
index 0000000..86aa29d
--- /dev/null
@@ -0,0 +1,10 @@
+/* Copyright (c) 2021 Tom Hebb (https://github.com/tchebb/parse-android-dynparts)
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * SPDX-License-Identifier: Apache-2.0 */
+
+#pragma once
+
+#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
diff --git a/src/dynamic-partitions/parse-dynparts/main.cpp b/src/dynamic-partitions/parse-dynparts/main.cpp
new file mode 100644 (file)
index 0000000..35fe9b5
--- /dev/null
@@ -0,0 +1,43 @@
+/* Copyright (c) 2021 Tom Hebb (https://github.com/tchebb/parse-android-dynparts)
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * SPDX-License-Identifier: Apache-2.0 */
+
+#include <liblp/liblp.h>
+
+#include <iostream>
+#include <sstream>
+#include <string_view>
+
+#include "lib.hpp"
+
+using namespace android::fs_mgr;
+
+using std::cerr;
+using std::cout;
+using std::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;
+    return 1;
+  }
+
+  bool list_tables = (argc == 3) ? true : false;
+
+  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);
+  if (out)
+    cout << *out << "\n";
+  else
+    return 1;
+
+  return 0;
+}
diff --git a/src/dynamic-partitions/parse-dynparts/test/test.cpp b/src/dynamic-partitions/parse-dynparts/test/test.cpp
new file mode 100644 (file)
index 0000000..4b356dd
--- /dev/null
@@ -0,0 +1,897 @@
+/* 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.
+ *
+ * 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.
+ *
+ * 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. */
+
+#include <iostream>
+#include <gtest/gtest.h>
+#include <string>
+#include "../lib.hpp"
+#include "../../testlib/metadataio.h"
+
+using namespace android::fs_mgr;
+
+// Empty metadata
+TEST(ParseDynpartsTest, EmptyMetadata) {
+    LpMetadata metadata {};
+    std::ostringstream messages;
+    EXPECT_EQ(go(metadata, false, "test", messages), "");
+    EXPECT_EQ(messages.str(), "");
+
+    EXPECT_EQ(go(metadata, true, "test", messages), "");
+    EXPECT_EQ(messages.str(), "");
+}
+
+// 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";
+    metadataio::readMetadata(metadata, metadata_str);
+#else
+   addPartitions(metadata, 1, 1);
+#endif
+
+    std::string output = "part_1_a,,,rw,0 10240 linear test 2048";
+    std::string output_list_tables = "part_1_a 0 10240 linear test 2048";
+    std::ostringstream messages;
+
+    EXPECT_EQ(go(metadata, false, "test", messages), output);
+    EXPECT_EQ(messages.str(), "");
+
+    EXPECT_EQ(go(metadata, true, "test", messages), output_list_tables);
+    EXPECT_EQ(messages.str(), "");
+}
+
+// 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  ";
+    metadataio::readMetadata(metadata, metadata_str);
+#else
+    addPartitions(metadata, 1, 2);
+#endif
+
+    std::string output = "part_1_a,,,rw,0 10240 linear test 2048;part_2_a,,,rw,0 10240 linear test 12288";
+    std::string output_list_tables = "part_1_a 0 10240 linear test 2048\npart_2_a 0 10240 linear test 12288";
+    std::ostringstream messages;
+
+    EXPECT_EQ(go(metadata, false, "test", messages), output);
+    EXPECT_EQ(messages.str(), "");
+
+    EXPECT_EQ(go(metadata, true, "test", messages), output_list_tables);
+    EXPECT_EQ(messages.str(), "");
+}
+
+// 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  ";
+    metadataio::readMetadata(metadata, metadata_str);
+#else
+    addPartitions(metadata, 1, 3);
+#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";
+    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";
+    std::ostringstream messages;
+
+    EXPECT_EQ(go(metadata, false, "test", messages), output);
+    EXPECT_EQ(messages.str(), "");
+
+    EXPECT_EQ(go(metadata, true, "test", messages), output_list_tables);
+    EXPECT_EQ(messages.str(), "");
+}
+
+// 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";
+    metadataio::readMetadata(metadata, metadata_str);
+#else
+    addPartitions(metadata, 1, 4);
+#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";
+    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";
+    std::ostringstream messages;
+
+    EXPECT_EQ(go(metadata, false, "test", messages), output);
+    EXPECT_EQ(messages.str(), "");
+
+    EXPECT_EQ(go(metadata, true, "test", messages), output_list_tables);
+    EXPECT_EQ(messages.str(), "");
+}
+
+// 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";
+    metadataio::readMetadata(metadata, metadata_str);
+#else
+    addPartitions(metadata, 2, 1);
+#endif
+
+    std::string output = "part_1_a,,,rw,0 10240 linear test 2048;part_1_b,,,rw,0 10240 linear test 12288";
+    std::string output_list_tables = "part_1_a 0 10240 linear test 2048\npart_1_b 0 10240 linear test 12288";
+    std::ostringstream messages;
+
+    EXPECT_EQ(go(metadata, false, "test", messages), output);
+    EXPECT_EQ(messages.str(), "");
+
+    EXPECT_EQ(go(metadata, true, "test", messages), output_list_tables);
+    EXPECT_EQ(messages.str(), "");
+}
+
+// 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";
+    metadataio::readMetadata(metadata, metadata_str);
+#else
+    addPartitions(metadata, 2, 2);
+#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";
+    std::string output_list_tables = "part_1_a 0 10240 linear test 2048\npart_2_a 0 10240 linear test 12288\npart_1_b 0 10240 linear test 22528\npart_2_b 0 10240 linear test 32768";
+    std::ostringstream messages;
+
+    EXPECT_EQ(go(metadata, false, "test", messages), output);
+    EXPECT_EQ(messages.str(), "");
+
+    EXPECT_EQ(go(metadata, true, "test", messages), output_list_tables);
+    EXPECT_EQ(messages.str(), "");
+}
+
+// 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";
+    metadataio::readMetadata(metadata, metadata_str);
+#else
+    addPartitions(metadata, 2, 3);
+#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";
+    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_1_b 0 10240 linear test 32768\npart_2_b 0 10240 linear test 43008\npart_3_b 0 10240 linear test 53248";
+    std::ostringstream messages;
+
+    EXPECT_EQ(go(metadata, false, "test", messages), output);
+    EXPECT_EQ(messages.str(), "");
+
+    EXPECT_EQ(go(metadata, true, "test", messages), output_list_tables);
+    EXPECT_EQ(messages.str(), "");
+}
+
+// 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";
+    metadataio::readMetadata(metadata, metadata_str);
+#else
+    addPartitions(metadata, 2, 4);
+#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";
+    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_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";
+    std::ostringstream messages;
+
+    EXPECT_EQ(go(metadata, false, "test", messages), output);
+    EXPECT_EQ(messages.str(), "");
+
+    EXPECT_EQ(go(metadata, true, "test", messages), output_list_tables);
+    EXPECT_EQ(messages.str(), "");
+}
+
+// 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";
+    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;
+
+    metadata.extents[0].target_data = 12288;
+
+    metadata.extents[1].num_sectors = 2048;
+    metadata.extents[1].target_data = 2048;
+
+    metadata.extents[2].num_sectors = 2048;
+    metadata.extents[2].target_data = 4096;
+
+    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);
+#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::ostringstream messages;
+
+    EXPECT_EQ(go(metadata, false, "test", messages), output);
+    EXPECT_EQ(messages.str(), "");
+
+    EXPECT_EQ(go(metadata, true, "test", messages), output_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";
+    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);
+#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::ostringstream messages;
+
+    EXPECT_EQ(go(metadata, false, "test", messages), output);
+    EXPECT_EQ(messages.str(), "");
+
+    EXPECT_EQ(go(metadata, true, "test", messages), output_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";
+    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);
+#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::ostringstream messages;
+
+    EXPECT_EQ(go(metadata, false, "test", messages), output);
+    EXPECT_EQ(messages.str(), "");
+
+    EXPECT_EQ(go(metadata, true, "test", messages), output_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";
+    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);
+
+    strncpy(metadata.partitions[4].name, "part_1_a", 36);
+    metadata.partitions[4].group_index = 1;
+
+    strncpy(metadata.partitions[5].name, "part_3_a", 36);
+    metadata.partitions[5].group_index = 1;
+
+    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);
+
+    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);
+#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::ostringstream messages;
+
+    EXPECT_EQ(go(metadata, false, "test", messages), output);
+    EXPECT_EQ(messages.str(), "");
+
+    EXPECT_EQ(go(metadata, true, "test", messages), output_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";
+    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;
+
+    metadata.extents[4].num_sectors = 2048;
+    metadata.extents[4].target_data = 32768;
+
+    metadata.extents[5].num_sectors = 2048;
+    metadata.extents[5].target_data = 34816;
+
+    LpMetadataExtent e;
+    e.num_sectors = 6144;
+    e.target_type = 0;
+    e.target_data = 36864;
+    e.target_source = 0;
+
+    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);
+#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::ostringstream messages;
+
+    EXPECT_EQ(go(metadata, false, "test", messages), output);
+    EXPECT_EQ(messages.str(), "");
+
+    EXPECT_EQ(go(metadata, true, "test", messages), output_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";
+    metadataio::readMetadata(metadata, metadata_str);
+#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);
+#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(), "");
+
+    EXPECT_EQ(go(metadata, true, "test", messages), output_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";
+    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);
+#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::ostringstream messages;
+
+    EXPECT_EQ(go(metadata, false, "test", messages), output);
+    EXPECT_EQ(messages.str(), "");
+
+    EXPECT_EQ(go(metadata, true, "test", messages), output_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";
+    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);
+#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::ostringstream messages;
+
+    EXPECT_EQ(go(metadata, false, "test", messages), output);
+    EXPECT_EQ(messages.str(), "");
+
+    EXPECT_EQ(go(metadata, true, "test", messages), output_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";
+    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::ostringstream messages;
+
+    EXPECT_EQ(go(metadata, false, "test", messages), output);
+    EXPECT_EQ(messages.str(), "");
+
+    EXPECT_EQ(go(metadata, true, "test", messages), output_list_tables);
+    EXPECT_EQ(messages.str(), "");
+}
\ No newline at end of file
diff --git a/src/dynamic-partitions/resize-dynparts/CMakeLists.txt b/src/dynamic-partitions/resize-dynparts/CMakeLists.txt
new file mode 100644 (file)
index 0000000..54ee949
--- /dev/null
@@ -0,0 +1,19 @@
+SET(CMAKE_CXX_STANDARD 17)
+SET(CMAKE_CXX_STANDARD_REQUIRED True)
+
+SET(RESIZEDYNPARTSSRCS main.cpp lib.cpp)
+SET(RESIZEDYNPARTSEXENAME "resize-dynparts")
+
+ADD_EXECUTABLE(${RESIZEDYNPARTSEXENAME} ${RESIZEDYNPARTSSRCS})
+TARGET_COMPILE_OPTIONS(${RESIZEDYNPARTSEXENAME} PRIVATE -Wall -Wextra -pedantic -fPIE)
+
+TARGET_LINK_LIBRARIES(${RESIZEDYNPARTSEXENAME} PRIVATE lp)
+
+INSTALL(TARGETS ${RESIZEDYNPARTSEXENAME} DESTINATION ${BINDIR})
+
+add_executable(resize-dynparts-test test/test.cpp lib.cpp ../testlib/metadataio.h ../testlib/metadataio.cpp)
+target_compile_options(resize-dynparts-test PRIVATE -Wall -Wextra -pedantic -fPIE -Wno-stringop-truncation)
+target_link_libraries(resize-dynparts-test PRIVATE GTest::gtest_main lp)
+
+include(GoogleTest)
+gtest_discover_tests(resize-dynparts-test)
diff --git a/src/dynamic-partitions/resize-dynparts/lib.cpp b/src/dynamic-partitions/resize-dynparts/lib.cpp
new file mode 100644 (file)
index 0000000..1299ef5
--- /dev/null
@@ -0,0 +1,150 @@
+/* Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ * SPDX-License-Identifier: Apache-2.0 */
+
+#include "lib.hpp"
+
+#include <algorithm>
+#include <fstream>
+#include <memory>
+#include <ostream>
+#include <string>
+#include <sstream>
+#include <vector>
+
+using namespace android::fs_mgr;
+using std::endl;
+using std::string;
+using std::stringstream;
+using std::unique_ptr;
+using std::vector;
+using std::ostream;
+using std::ifstream;
+
+/*
+bool readFromUpdateCfgFile(const string &filename, vector<update_cfg_record> &update_cfg_records, ostream &messages) {
+    ifstream ios;
+    ios.open(filename);
+
+    if (ios.good()) {
+        update_cfg_record r;
+
+        while (ss >> r.part_name >> r.part_image_name >> r.upgrade_type >> r.part_dev
+                  >> r.offset >> r.old_size >> r.new_size >> r.old_hash >> r.new_hash)
+            update_cfg_records.push_back(r);
+    } else {
+        messages << "Could not open " << filename;
+        return false;
+    }
+
+    return true;
+}
+*/
+
+bool readFromUpdateCfgFile(const string &filename, vector<update_cfg_record> &update_cfg_records, ostream &messages) {
+    update_cfg_records.clear();
+    ifstream ios;
+    ios.open(filename);
+
+    if (ios.good()) {
+        string line;
+        update_cfg_record r;
+
+        while (getline(ios, line)) {
+            if(!line.empty()) {
+                stringstream ss(line);
+
+                if (ss >> r.part_name >> r.part_image_name >> r.upgrade_type >> r.part_dev
+                        >> r.offset >> r.old_size >> r.new_size >> r.old_hash >> r.new_hash) {
+                    update_cfg_records.push_back(r);
+                } else {
+                    messages << "Read from " << filename << " failed";
+                    return false;
+                }
+            }
+        }
+    } else {
+        messages << "Could not open " << filename;
+        return false;
+    }
+
+    if(!update_cfg_records.size()) {
+        messages << "Read from " << filename << " failed";
+        return false;
+    }
+
+    return true;
+}
+
+bool performOpResize(const std::string &part_name, uint64_t new_size, std::unique_ptr<MetadataBuilder> &builder, std::ostream &messages) {
+    auto partition = builder->FindPartition(part_name);
+
+    if (partition == nullptr) {
+        messages << "Failed to find partition " << part_name
+             << " in dynamic partition metadata.";
+        return false;
+    }
+
+    if (!builder->ResizePartition(partition, new_size)) {
+        messages << "Failed to resize partition " << part_name << " to size " << new_size
+             << ".";
+        return false;
+    }
+    return true;
+}
+
+unique_ptr<android::fs_mgr::LpMetadata> go(const android::fs_mgr::LpMetadata &metadata, const std::string &slot,
+        const vector<update_cfg_record> &update_cfg_records, std::ostream &messages) {    
+    auto builder = MetadataBuilder::New(metadata);
+
+    if (!builder) {
+        messages << "Could not create MetadataBuilder" << "\n";
+        return nullptr;
+    }
+
+    string suffix = "_" + slot;
+    string part_name;
+
+    for (const auto& p : metadata.partitions) {
+        // LpMetadataPartition.name (char[36]) can have exactly 36 characters with
+        // no trailing zero so any string related function wouldn't work properly.
+        // String has to be constructed using GetPartitionName
+        part_name = GetPartitionName(p);
+
+        if (part_name.substr(part_name.size() - 2, 2) == suffix) {
+            uint64_t new_size = 0;
+
+            if (update_cfg_records.size()) { // update according to update.cfg
+                auto it = std::find_if(update_cfg_records.begin(), update_cfg_records.end(), [part_name](const update_cfg_record &a) {
+                    return part_name == a.part_name || part_name.substr(0, part_name.size() - 2) == a.part_name;
+                });
+
+                if (it != update_cfg_records.end()) {
+                    new_size = it->new_size;
+                } else {
+                    continue;
+                }
+            } else {
+                string other_part_name = part_name;
+                other_part_name[other_part_name.size() - 1] = (slot == "a") ? 'b' : 'a';
+                auto other_part = FindPartition(metadata, other_part_name);
+                new_size = GetPartitionSize(metadata, *other_part);
+            }
+
+            if (new_size != GetPartitionSize(metadata, p)) {
+                if (!performOpResize(part_name, new_size, builder, messages)) {
+                    messages << "Could not perform resize operation of " << part_name << endl;
+                    return nullptr;
+                }
+            }
+        }
+    }
+
+    auto metadata_new = builder->Export();
+
+    if (metadata_new == nullptr) {
+        messages << "Failed to export metadata.";
+        return nullptr;
+    }
+
+    return metadata_new;
+}
diff --git a/src/dynamic-partitions/resize-dynparts/lib.hpp b/src/dynamic-partitions/resize-dynparts/lib.hpp
new file mode 100644 (file)
index 0000000..4a12c83
--- /dev/null
@@ -0,0 +1,27 @@
+/* Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ * SPDX-License-Identifier: Apache-2.0 */
+
+#pragma once
+
+#include <liblp/liblp.h>
+#include <liblp/builder.h>
+
+struct update_cfg_record {
+    std::string part_name;
+    std::string part_image_name;
+    std::string upgrade_type;
+    std::string part_dev;
+    uint64_t offset;
+    uint64_t old_size;
+    uint64_t new_size;
+    std::string old_hash;
+    std::string new_hash;
+};
+
+bool readFromUpdateCfgFile(const std::string &filename, std::vector<update_cfg_record> &update_cfg_records,
+        std::ostream &messages);
+
+bool performOpResize(const std::string &part_name, uint64_t new_size, std::unique_ptr<android::fs_mgr::MetadataBuilder> &builder , std::ostream &messages);
+
+std::unique_ptr<android::fs_mgr::LpMetadata> go(const android::fs_mgr::LpMetadata &metadata, const std::string &slot,
+        const std::vector<update_cfg_record> &update_cfg_records, std::ostream &messages);
\ No newline at end of file
diff --git a/src/dynamic-partitions/resize-dynparts/main.cpp b/src/dynamic-partitions/resize-dynparts/main.cpp
new file mode 100644 (file)
index 0000000..6a18660
--- /dev/null
@@ -0,0 +1,66 @@
+/* Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ * SPDX-License-Identifier: Apache-2.0 */
+
+#include <iostream>
+#include <string>
+#include <vector>
+#include <liblp/liblp.h>
+#include "lib.hpp"
+
+using namespace android::fs_mgr;
+
+using std::cerr;
+using std::cout;
+using std::endl;
+using std::string;
+using std::vector;
+
+int main(int argc, char* argv[]) {
+    if (argc < 3 || argc > 4) {
+        cerr << "Modify metadata on a super partition by resizing partitions"
+             << " from a given slot. Metadata about size of partitions will be"
+             << " copied from another slot unless update.cfg file is given."
+             << " In such case metadata is modified according to this file."
+             << endl
+             << "Usage:"
+             << argv[0] << " super_path a|b [update_cfg_path]" << endl
+             << "where:" << endl
+             << "      super_path: path to block device or file containing"
+             << " the super partition" << endl
+             << "      a|b: Partitions from this slot will be resized" << endl
+             << "      update_cfg_path: optional. If provided, information"
+             << " about new size of partitions will be read from the pointed"
+             << " update.cfg file generated during creation of delta.tar"
+             << " instead of metadata from opposite slot" << endl;
+        return 1;
+    }
+
+    string super_path = argv[1];
+    string slot = argv[2];
+    string update_cfg_path = (argc == 4) ? argv[3] : "";
+
+    vector<update_cfg_record> update_cfg_records;
+
+    if (update_cfg_path != "" && !readFromUpdateCfgFile(update_cfg_path, update_cfg_records, cerr)) {
+        cerr << "Could not read from " << update_cfg_path << endl;
+        return 1;
+    }
+
+    auto metadata = android::fs_mgr::ReadMetadata(super_path, 0);
+
+    if (!metadata) {
+        cerr << "Could not read super partition metadata for " << super_path << "\n";
+        return 1;
+    }
+
+    auto metadata_new = go(*metadata, slot, update_cfg_records, cerr);
+
+    if (metadata_new == nullptr) {
+        return 1;
+    }
+
+    if (!UpdatePartitionTable(super_path, *metadata_new, 0)) {
+        cerr << "Failed to write metadata.";
+        return 1;
+    }
+}
diff --git a/src/dynamic-partitions/resize-dynparts/test/test.cpp b/src/dynamic-partitions/resize-dynparts/test/test.cpp
new file mode 100644 (file)
index 0000000..122192c
--- /dev/null
@@ -0,0 +1,686 @@
+/* Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * SPDX-License-Identifier: MIT
+ *
+ * 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, super_dump tool was used to save LpMetadata of super image as a
+ * string. Those strings are used in below tests to create test LpMetadata.
+ * Super images were generated with 
+ * ../../testlib/gemerate_test_data_for_resizing.sh script
+ */
+
+#include <cstdlib>
+#include <iostream>
+#include <string>
+#include <vector>
+#include <gtest/gtest.h>
+#include "../lib.hpp"
+#include <liblp/liblp.h>
+#include <liblp/builder.h>
+#include "../../testlib/metadataio.h"
+
+using std::vector;
+using std::ostringstream;
+
+// Try to read non-existing update.cfg
+TEST(ResizeDynpartsTest, ReadNonExistingUpdateCfg) {
+    ostringstream messages;
+    vector<update_cfg_record> update_cfg_records;
+    
+    EXPECT_EQ(readFromUpdateCfgFile("paththatdoesnotexist", update_cfg_records, messages), false);
+    EXPECT_EQ(messages.str(), "Could not open paththatdoesnotexist");
+}
+
+// Try to read empty update.cfg
+TEST(ResizeDynpartsTest, ReadEmptyUpdateCfg) {
+    ostringstream messages;
+    vector<update_cfg_record> update_cfg_records;
+    
+    EXPECT_EQ(readFromUpdateCfgFile("test/update_empty.cfg", update_cfg_records, messages), false);
+    EXPECT_EQ(messages.str(), "Read from test/update_empty.cfg failed");
+    EXPECT_EQ(update_cfg_records.size(), 0);
+}
+
+// Try to read update.cfg with malformed data
+TEST(ResizeDynpartsTest, ReadMalformedUpdateCfg) {
+    ostringstream messages;
+    vector<update_cfg_record> update_cfg_records;
+    
+    EXPECT_EQ(readFromUpdateCfgFile("test/update_malformed.cfg", update_cfg_records, messages), false);
+    EXPECT_EQ(messages.str(), "Read from test/update_malformed.cfg failed");
+    EXPECT_EQ(update_cfg_records.size(), 0);
+}
+
+// Try to read ariticially generated update.cfg
+TEST(ResizeDynpartsTest, ReadArtificialUpdateCfg) {
+    ostringstream messages;
+    vector<update_cfg_record> update_cfg_records;
+    
+    EXPECT_EQ(readFromUpdateCfgFile("test/update_artificial.cfg", update_cfg_records, messages), true);
+    EXPECT_EQ(messages.str(), "");
+    EXPECT_EQ(update_cfg_records.size(), 6);
+
+    for (size_t i = 0; i < update_cfg_records.size(); ++i) {
+        EXPECT_EQ(update_cfg_records[i].part_name, "part_name_" + std::to_string(i));
+        EXPECT_EQ(update_cfg_records[i].part_image_name, "part_image_name_" + std::to_string(i));
+        EXPECT_EQ(update_cfg_records[i].upgrade_type, "upgrade_type_" + std::to_string(i));
+        EXPECT_EQ(update_cfg_records[i].part_dev, "part_dev_" + std::to_string(i));
+        EXPECT_EQ(update_cfg_records[i].offset, i);
+        EXPECT_EQ(update_cfg_records[i].old_size, 8 * (i + 1));
+        EXPECT_EQ(update_cfg_records[i].new_size, 128 * (i + 1));
+        EXPECT_EQ(update_cfg_records[i].old_hash, "old_hash_" + std::to_string(i));
+        EXPECT_EQ(update_cfg_records[i].new_hash, "new_hash_" + std::to_string(i));
+    }
+}
+
+// Try to read real update.cfg
+TEST(ResizeDynpartsTest, ReadUpdateCfg) {
+    ostringstream messages;
+    vector<update_cfg_record> update_cfg_records;
+    
+    EXPECT_EQ(readFromUpdateCfgFile("test/update_real.cfg", update_cfg_records, messages), true);
+    EXPECT_EQ(messages.str(), "");
+    EXPECT_EQ(update_cfg_records.size(), 6);
+
+    EXPECT_EQ(update_cfg_records[0].part_name, "BOOT");
+    EXPECT_EQ(update_cfg_records[0].part_image_name, "boot.img");
+    EXPECT_EQ(update_cfg_records[0].upgrade_type, "FULL_IMAGE:BEFORE_BOOT_FOTA");
+    EXPECT_EQ(update_cfg_records[0].part_dev, "/dev/mmcblk0p1");
+    EXPECT_EQ(update_cfg_records[0].offset, 0);
+    EXPECT_EQ(update_cfg_records[0].old_size, 67108864);
+    EXPECT_EQ(update_cfg_records[0].new_size, 67108864);
+    EXPECT_EQ(update_cfg_records[0].old_hash, "888f83a2d8ed4ce1a546bcad59f10cd73426a83a");
+    EXPECT_EQ(update_cfg_records[0].new_hash, "64fa0e0dfb78609b314e8d050fd7f138f26ef2a4");
+
+    EXPECT_EQ(update_cfg_records[1].part_name, "hal");
+    EXPECT_EQ(update_cfg_records[1].part_image_name, "hal.img");
+    EXPECT_EQ(update_cfg_records[1].upgrade_type, "DELTA_IMAGE:AT_BOOT_FOTA");
+    EXPECT_EQ(update_cfg_records[1].part_dev, "/dev/mmcblk0p10");
+    EXPECT_EQ(update_cfg_records[1].offset, 0);
+    EXPECT_EQ(update_cfg_records[1].old_size, 107216896);
+    EXPECT_EQ(update_cfg_records[1].new_size, 107216896);
+    EXPECT_EQ(update_cfg_records[1].old_hash, "f5b68d272f8a7849d64d6ec9c95052578d969d5a");
+    EXPECT_EQ(update_cfg_records[1].new_hash, "c7bb4ae65822cf1d7c16342d58241a32e720677a");
+
+    EXPECT_EQ(update_cfg_records[2].part_name, "modules");
+    EXPECT_EQ(update_cfg_records[2].part_image_name, "modules.img");
+    EXPECT_EQ(update_cfg_records[2].upgrade_type, "FULL_IMAGE:BEFORE_BOOT_FOTA");
+    EXPECT_EQ(update_cfg_records[2].part_dev, "/dev/mmcblk0p6");
+    EXPECT_EQ(update_cfg_records[2].offset, 0);
+    EXPECT_EQ(update_cfg_records[2].old_size, 20971520);
+    EXPECT_EQ(update_cfg_records[2].new_size, 20971520);
+    EXPECT_EQ(update_cfg_records[2].old_hash, "094f2d2539d010c7cf379c78e1893b715b2c5446");
+    EXPECT_EQ(update_cfg_records[2].new_hash, "6beb947291abcba23e828631a45f0d854116bb17");
+
+    EXPECT_EQ(update_cfg_records[3].part_name, "rootfs");
+    EXPECT_EQ(update_cfg_records[3].part_image_name, "rootfs.img");
+    EXPECT_EQ(update_cfg_records[3].upgrade_type, "DELTA_IMAGE:AT_BOOT_FOTA");
+    EXPECT_EQ(update_cfg_records[3].part_dev, "/dev/mmcblk0p2");
+    EXPECT_EQ(update_cfg_records[3].offset, 0);
+    EXPECT_EQ(update_cfg_records[3].old_size, 627478528);
+    EXPECT_EQ(update_cfg_records[3].new_size, 627486720);
+    EXPECT_EQ(update_cfg_records[3].old_hash, "bf0ca2d9927c64531ca7899ee88202d09a57a9e8");
+    EXPECT_EQ(update_cfg_records[3].new_hash, "cb2f79550766198597baaff5127c97a274697211");
+
+    EXPECT_EQ(update_cfg_records[4].part_name, "ramdisk");
+    EXPECT_EQ(update_cfg_records[4].part_image_name, "ramdisk.img");
+    EXPECT_EQ(update_cfg_records[4].upgrade_type, "FULL_IMAGE:AT_BOOT_FOTA");
+    EXPECT_EQ(update_cfg_records[4].part_dev, "/dev/mmcblk0p7");
+    EXPECT_EQ(update_cfg_records[4].offset, 0);
+    EXPECT_EQ(update_cfg_records[4].old_size, 22902784);
+    EXPECT_EQ(update_cfg_records[4].new_size, 22902784);
+    EXPECT_EQ(update_cfg_records[4].old_hash, "3ed11fab9a4845faafecea55a6dd4c8cdb5f3d57");
+    EXPECT_EQ(update_cfg_records[4].new_hash, "faa0aabc206c65bb42eeb1e453222347228658a6");
+
+    EXPECT_EQ(update_cfg_records[5].part_name, "ramdisk-recovery");
+    EXPECT_EQ(update_cfg_records[5].part_image_name, "ramdisk-recovery.img");
+    EXPECT_EQ(update_cfg_records[5].upgrade_type, "FULL_IMAGE:BEFORE_BOOT_FOTA");
+    EXPECT_EQ(update_cfg_records[5].part_dev, "/dev/mmcblk0p8");
+    EXPECT_EQ(update_cfg_records[5].offset, 0);
+    EXPECT_EQ(update_cfg_records[5].old_size, 26178560);
+    EXPECT_EQ(update_cfg_records[5].new_size, 26178560);
+    EXPECT_EQ(update_cfg_records[5].old_hash, "67386cad4b5773240730134421ea8b91f57e2d15");
+    EXPECT_EQ(update_cfg_records[5].new_hash, "52bb203644adabb9fa3f8386159b8dcd91372971");
+}
+
+// Try to read ariticially generated update.cfg
+TEST(ResizeDynpartsTest, ReadUpdateCfg1) {
+    ostringstream messages;
+    vector<update_cfg_record> update_cfg_records;
+    
+    EXPECT_EQ(readFromUpdateCfgFile("test/update_1.cfg", update_cfg_records, messages), true);
+    EXPECT_EQ(messages.str(), "");
+    EXPECT_EQ(update_cfg_records.size(), 1);
+
+    EXPECT_EQ(update_cfg_records[0].part_name, "part_1");
+    EXPECT_EQ(update_cfg_records[0].part_image_name, "part_image_name");
+    EXPECT_EQ(update_cfg_records[0].upgrade_type, "upgrade_type");
+    EXPECT_EQ(update_cfg_records[0].part_dev, "part_dev");
+    EXPECT_EQ(update_cfg_records[0].offset, 0);
+    EXPECT_EQ(update_cfg_records[0].old_size, 1024 * 1024);
+    EXPECT_EQ(update_cfg_records[0].new_size, 2 * 1024 * 1024);
+    EXPECT_EQ(update_cfg_records[0].old_hash, "old_hash");
+    EXPECT_EQ(update_cfg_records[0].new_hash, "new_hash");
+}
+
+// Try to read ariticially generated update.cfg
+TEST(ResizeDynpartsTest, ReadUpdateCfg2) {
+    ostringstream messages;
+    vector<update_cfg_record> update_cfg_records;
+    
+    EXPECT_EQ(readFromUpdateCfgFile("test/update_2.cfg", update_cfg_records, messages), true);
+    EXPECT_EQ(messages.str(), "");
+    EXPECT_EQ(update_cfg_records.size(), 2);
+
+    EXPECT_EQ(update_cfg_records[0].part_name, "part_1");
+    EXPECT_EQ(update_cfg_records[0].part_image_name, "part_image_name");
+    EXPECT_EQ(update_cfg_records[0].upgrade_type, "upgrade_type");
+    EXPECT_EQ(update_cfg_records[0].part_dev, "part_dev");
+    EXPECT_EQ(update_cfg_records[0].offset, 0);
+    EXPECT_EQ(update_cfg_records[0].old_size, 1024 * 1024);
+    EXPECT_EQ(update_cfg_records[0].new_size, 2 * 1024 * 1024);
+    EXPECT_EQ(update_cfg_records[0].old_hash, "old_hash");
+    EXPECT_EQ(update_cfg_records[0].new_hash, "new_hash");
+
+    EXPECT_EQ(update_cfg_records[1].part_name, "part_2");
+    EXPECT_EQ(update_cfg_records[1].part_image_name, "part_image_name");
+    EXPECT_EQ(update_cfg_records[1].upgrade_type, "upgrade_type");
+    EXPECT_EQ(update_cfg_records[1].part_dev, "part_dev");
+    EXPECT_EQ(update_cfg_records[1].offset, 0);
+    EXPECT_EQ(update_cfg_records[1].old_size, 1024 * 1024);
+    EXPECT_EQ(update_cfg_records[1].new_size, 3 * 1024 * 1024);
+    EXPECT_EQ(update_cfg_records[1].old_hash, "old_hash");
+    EXPECT_EQ(update_cfg_records[1].new_hash, "new_hash");
+}
+
+// Try to read resize partitions. Two slots, one partition each,
+// size(part_1_a) == size(part_1_b)
+TEST(ResizeDynpartsTest, CopyPartitionsTwoSlotsOnePartitionEachEqualSize) {
+    ostringstream messages;
+    vector<update_cfg_record> update_cfg_records;
+    android::fs_mgr::LpMetadata metadata;
+
+    // super_resize_1.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  140 65 72 9 124 21 26 115 74 228 102 111 44 13 51 128 10 39 179 62 92 24 236 250 188 121 215 139 224 95 40 194 202 195 160 23 219 128 80 127 39 62 29 61 16 250 52 94 241 221 214 40 58 221 16 55 213 246 60 218 161 122 9 117 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 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 2048 0 2048 0  2048 0 4096 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 10977280 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 10977280 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";
+    metadataio::readMetadata(metadata, metadata_str);
+
+    auto part_1_a = FindPartition(metadata, "part_1_a");
+    auto part_1_b = FindPartition(metadata, "part_1_b");
+    auto part_1_a_size = GetPartitionSize(metadata, *part_1_a);
+    auto part_1_b_size = GetPartitionSize(metadata, *part_1_b);
+    EXPECT_EQ(part_1_a_size, 1024*1024); // should be 1 MiB
+    EXPECT_EQ(part_1_b_size, 1024*1024); // should be 1 MiB
+    EXPECT_EQ(part_1_a_size, part_1_b_size); // should be equal
+
+    // COPY FROM A TO B
+    auto metadata_mod = go(metadata, "b", update_cfg_records, messages);
+    
+    EXPECT_NE(metadata_mod, nullptr);
+
+    auto part_1_a_mod = FindPartition(*metadata_mod, "part_1_a");
+    auto part_1_b_mod = FindPartition(*metadata_mod, "part_1_b");
+    auto part_1_a_mod_size = GetPartitionSize(*metadata_mod, *part_1_a_mod);
+    auto part_1_b_mod_size = GetPartitionSize(*metadata_mod, *part_1_b_mod);
+
+
+    EXPECT_EQ(part_1_a_mod_size, part_1_a_size);
+    EXPECT_EQ(part_1_b_mod_size, part_1_a_size);
+    EXPECT_EQ(part_1_a_mod_size, part_1_b_mod_size);
+
+    // COPY FROM B TO A
+    metadata_mod = go(metadata, "a", update_cfg_records, messages);
+    
+    EXPECT_NE(metadata_mod, nullptr);
+
+    part_1_a_mod = FindPartition(*metadata_mod, "part_1_a");
+    part_1_b_mod = FindPartition(*metadata_mod, "part_1_b");
+    part_1_a_mod_size = GetPartitionSize(*metadata_mod, *part_1_a_mod);
+    part_1_b_mod_size = GetPartitionSize(*metadata_mod, *part_1_b_mod);
+
+
+    EXPECT_EQ(part_1_a_mod_size, part_1_b_size);
+    EXPECT_EQ(part_1_b_mod_size, part_1_b_size);
+    EXPECT_EQ(part_1_a_mod_size, part_1_b_mod_size);
+
+    // RESIZE A USING UPDATE.CFG
+    EXPECT_EQ(readFromUpdateCfgFile("test/update_1.cfg", update_cfg_records, messages), true);
+    EXPECT_EQ(update_cfg_records.size(), 1);
+    EXPECT_EQ(update_cfg_records[0].new_size, 2*1024*1024);
+
+    metadata_mod = go(metadata, "a", update_cfg_records, messages);
+
+    EXPECT_NE(metadata_mod, nullptr);
+
+    part_1_a_mod = FindPartition(*metadata_mod, "part_1_a");
+    part_1_b_mod = FindPartition(*metadata_mod, "part_1_b");
+    part_1_a_mod_size = GetPartitionSize(*metadata_mod, *part_1_a_mod);
+    part_1_b_mod_size = GetPartitionSize(*metadata_mod, *part_1_b_mod);
+
+    EXPECT_EQ(part_1_a_mod_size, update_cfg_records[0].new_size);
+    EXPECT_EQ(part_1_b_mod_size, part_1_b_size);
+    EXPECT_NE(part_1_a_mod_size, part_1_b_mod_size);
+
+    // RESIZE B USING UPDATE.CFG
+    metadata_mod = go(metadata, "b", update_cfg_records, messages);
+
+    EXPECT_NE(metadata_mod, nullptr);
+
+    part_1_a_mod = FindPartition(*metadata_mod, "part_1_a");
+    part_1_b_mod = FindPartition(*metadata_mod, "part_1_b");
+    part_1_a_mod_size = GetPartitionSize(*metadata_mod, *part_1_a_mod);
+    part_1_b_mod_size = GetPartitionSize(*metadata_mod, *part_1_b_mod);
+
+    EXPECT_EQ(part_1_a_mod_size, part_1_a_size);
+    EXPECT_EQ(part_1_b_mod_size, update_cfg_records[0].new_size);
+    EXPECT_NE(part_1_a_mod_size, part_1_b_mod_size);
+}
+
+// Try to read resize partitions. Two slots, one partition each,
+// size(part_1_a) < size(part_1_b)
+TEST(ResizeDynpartsTest, CopyPartitionsTwoSlotsOnePartitionEachDifferentSize) {
+    ostringstream messages;
+    vector<update_cfg_record> update_cfg_records;
+    android::fs_mgr::LpMetadata metadata;
+    // super_resize_2.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  106 194 140 163 229 215 181 245 89 139 124 130 207 106 21 42 202 46 64 233 63 132 144 16 236 155 92 219 130 195 126 66 110 127 174 180 35 68 164 149 140 197 55 244 210 145 179 239 221 155 3 51 34 213 63 125 221 81 207 45 154 245 195 88 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 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 2048 0 2048 0  4096 0 4096 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 10977280 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 10977280 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";
+    metadataio::readMetadata(metadata, metadata_str);
+
+    auto part_1_a = FindPartition(metadata, "part_1_a");
+    auto part_1_b = FindPartition(metadata, "part_1_b");
+    auto part_1_a_size = GetPartitionSize(metadata, *part_1_a);
+    auto part_1_b_size = GetPartitionSize(metadata, *part_1_b);
+    EXPECT_EQ(part_1_a_size, 1024*1024); // should be 1 MiB
+    EXPECT_EQ(part_1_b_size, 2*1024*1024); // should be 2 MiB
+    EXPECT_NE(part_1_a_size, part_1_b_size); // should not be equal
+
+    // COPY FROM A TO B
+    auto metadata_mod = go(metadata, "b", update_cfg_records, messages);
+    
+    EXPECT_NE(metadata_mod, nullptr);
+
+    auto part_1_a_mod = FindPartition(*metadata_mod, "part_1_a");
+    auto part_1_b_mod = FindPartition(*metadata_mod, "part_1_b");
+    auto part_1_a_mod_size = GetPartitionSize(*metadata_mod, *part_1_a_mod);
+    auto part_1_b_mod_size = GetPartitionSize(*metadata_mod, *part_1_b_mod);
+
+
+    EXPECT_EQ(part_1_a_mod_size, part_1_a_size);
+    EXPECT_EQ(part_1_b_mod_size, part_1_a_size);
+    EXPECT_EQ(part_1_a_mod_size, part_1_b_mod_size);
+
+    // COPY FROM B TO A
+    metadata_mod = go(metadata, "a", update_cfg_records, messages);
+    
+    EXPECT_NE(metadata_mod, nullptr);
+
+    part_1_a_mod = FindPartition(*metadata_mod, "part_1_a");
+    part_1_b_mod = FindPartition(*metadata_mod, "part_1_b");
+    part_1_a_mod_size = GetPartitionSize(*metadata_mod, *part_1_a_mod);
+    part_1_b_mod_size = GetPartitionSize(*metadata_mod, *part_1_b_mod);
+
+
+    EXPECT_EQ(part_1_a_mod_size, part_1_b_size);
+    EXPECT_EQ(part_1_b_mod_size, part_1_b_size);
+    EXPECT_EQ(part_1_a_mod_size, part_1_b_mod_size);
+}
+
+// Try to resize partitions. Two slots, two partitions each
+// part_1_a == part_2_a == part_1_b == part_2_b
+TEST(ResizeDynpartsTest, CopyPartitionsTwoSlotsTwoPartitionsEachAllEqualSize) {
+    ostringstream messages;
+    vector<update_cfg_record> update_cfg_records;
+    android::fs_mgr::LpMetadata metadata;
+    // super_resize_3.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  226 25 14 69 34 22 46 18 115 60 16 93 119 203 231 234 183 33 48 144 39 252 92 54 242 166 120 113 69 77 226 63 219 169 163 99 109 191 146 46 117 150 168 90 42 67 35 44 89 74 163 46 126 120 178 174 157 251 163 158 50 21 144 12 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 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 2048 0 2048 0  2048 0 4096 0  2048 0 6144 0  2048 0 8192 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 10977280 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 10977280 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";
+    metadataio::readMetadata(metadata, metadata_str);
+
+    auto part_1_a = FindPartition(metadata, "part_1_a");
+    auto part_2_a = FindPartition(metadata, "part_2_a");
+    auto part_1_b = FindPartition(metadata, "part_1_b");
+    auto part_2_b = FindPartition(metadata, "part_2_b");
+    auto part_1_a_size = GetPartitionSize(metadata, *part_1_a);
+    auto part_2_a_size = GetPartitionSize(metadata, *part_2_a);
+    auto part_1_b_size = GetPartitionSize(metadata, *part_1_b);
+    auto part_2_b_size = GetPartitionSize(metadata, *part_2_b);
+    EXPECT_EQ(part_1_a_size, 1024*1024); // should be 1 MiB
+    EXPECT_EQ(part_2_a_size, 1024*1024); // should be 1 MiB
+    EXPECT_EQ(part_1_b_size, 1024*1024); // should be 1 MiB
+    EXPECT_EQ(part_2_b_size, 1024*1024); // should be 1 MiB
+    EXPECT_EQ(part_1_a_size, part_2_a_size); // should be equal
+    EXPECT_EQ(part_1_b_size, part_2_b_size); // should be equal
+    EXPECT_EQ(part_1_a_size, part_1_b_size); // should be equal
+    
+    // COPY FROM A TO B
+    auto metadata_mod = go(metadata, "b", update_cfg_records, messages);
+    
+    EXPECT_NE(metadata_mod, nullptr);
+
+    auto part_1_a_mod = FindPartition(*metadata_mod, "part_1_a");
+    auto part_2_a_mod = FindPartition(*metadata_mod, "part_2_a");
+    auto part_1_b_mod = FindPartition(*metadata_mod, "part_1_b");
+    auto part_2_b_mod = FindPartition(*metadata_mod, "part_2_b");
+    auto part_1_a_mod_size = GetPartitionSize(*metadata_mod, *part_1_a_mod);
+    auto part_2_a_mod_size = GetPartitionSize(*metadata_mod, *part_2_a_mod);
+    auto part_1_b_mod_size = GetPartitionSize(*metadata_mod, *part_1_b_mod);
+    auto part_2_b_mod_size = GetPartitionSize(*metadata_mod, *part_2_b_mod);
+
+    EXPECT_EQ(part_1_a_mod_size, part_1_a_size);
+    EXPECT_EQ(part_2_a_mod_size, part_2_a_size);
+    EXPECT_EQ(part_1_b_mod_size, part_1_a_size);
+    EXPECT_EQ(part_2_b_mod_size, part_2_a_size);
+    EXPECT_EQ(part_1_a_mod_size, part_1_b_mod_size);
+    EXPECT_EQ(part_2_a_mod_size, part_2_b_mod_size);
+
+    // COPY FROM B TO A
+    metadata_mod = go(metadata, "a", update_cfg_records, messages);
+    
+    EXPECT_NE(metadata_mod, nullptr);
+
+    part_1_a_mod = FindPartition(*metadata_mod, "part_1_a");
+    part_2_a_mod = FindPartition(*metadata_mod, "part_2_a");
+    part_1_b_mod = FindPartition(*metadata_mod, "part_1_b");
+    part_2_b_mod = FindPartition(*metadata_mod, "part_2_b");
+    part_1_a_mod_size = GetPartitionSize(*metadata_mod, *part_1_a_mod);
+    part_2_a_mod_size = GetPartitionSize(*metadata_mod, *part_2_a_mod);
+    part_1_b_mod_size = GetPartitionSize(*metadata_mod, *part_1_b_mod);
+    part_2_b_mod_size = GetPartitionSize(*metadata_mod, *part_2_b_mod);
+
+    EXPECT_EQ(part_1_a_mod_size, part_1_b_size);
+    EXPECT_EQ(part_2_a_mod_size, part_2_b_size);
+    EXPECT_EQ(part_1_b_mod_size, part_1_b_size);
+    EXPECT_EQ(part_2_b_mod_size, part_2_b_size);
+    EXPECT_EQ(part_1_a_mod_size, part_1_b_mod_size);
+    EXPECT_EQ(part_2_a_mod_size, part_2_b_mod_size);
+
+    // RESIZE A USING UPDATE_1.CFG
+    EXPECT_EQ(readFromUpdateCfgFile("test/update_1.cfg", update_cfg_records, messages), true);
+    EXPECT_EQ(update_cfg_records.size(), 1);
+    EXPECT_EQ(update_cfg_records[0].new_size, 2*1024*1024);
+
+    // update_1.cfg has only part_1 info. This should result in nullptr
+    metadata_mod = go(metadata, "a", update_cfg_records, messages);
+    
+    EXPECT_NE(metadata_mod, nullptr);
+
+    part_1_a_mod = FindPartition(*metadata_mod, "part_1_a");
+    part_2_a_mod = FindPartition(*metadata_mod, "part_2_a");
+    part_1_b_mod = FindPartition(*metadata_mod, "part_1_b");
+    part_2_b_mod = FindPartition(*metadata_mod, "part_2_b");
+    part_1_a_mod_size = GetPartitionSize(*metadata_mod, *part_1_a_mod);
+    part_2_a_mod_size = GetPartitionSize(*metadata_mod, *part_2_a_mod);
+    part_1_b_mod_size = GetPartitionSize(*metadata_mod, *part_1_b_mod);
+    part_2_b_mod_size = GetPartitionSize(*metadata_mod, *part_2_b_mod);
+
+    EXPECT_EQ(part_1_a_mod_size, update_cfg_records[0].new_size);
+    EXPECT_EQ(part_2_a_mod_size, part_2_a_size);
+    EXPECT_EQ(part_1_b_mod_size, part_1_b_size);
+    EXPECT_EQ(part_2_b_mod_size, part_2_b_size);
+
+    // RESIZE A USING UPDATE_2.CFG
+    EXPECT_EQ(readFromUpdateCfgFile("test/update_2.cfg", update_cfg_records, messages), true);
+    EXPECT_EQ(update_cfg_records.size(), 2);
+    EXPECT_EQ(update_cfg_records[0].new_size, 2*1024*1024);
+    EXPECT_EQ(update_cfg_records[1].new_size, 3*1024*1024);
+
+    metadata_mod = go(metadata, "a", update_cfg_records, messages);
+    
+    EXPECT_NE(metadata_mod, nullptr);
+
+    part_1_a_mod = FindPartition(*metadata_mod, "part_1_a");
+    part_2_a_mod = FindPartition(*metadata_mod, "part_2_a");
+    part_1_b_mod = FindPartition(*metadata_mod, "part_1_b");
+    part_2_b_mod = FindPartition(*metadata_mod, "part_2_b");
+    part_1_a_mod_size = GetPartitionSize(*metadata_mod, *part_1_a_mod);
+    part_2_a_mod_size = GetPartitionSize(*metadata_mod, *part_2_a_mod);
+    part_1_b_mod_size = GetPartitionSize(*metadata_mod, *part_1_b_mod);
+    part_2_b_mod_size = GetPartitionSize(*metadata_mod, *part_2_b_mod);
+
+    EXPECT_EQ(part_1_a_mod_size, update_cfg_records[0].new_size);
+    EXPECT_EQ(part_2_a_mod_size, update_cfg_records[1].new_size);
+    EXPECT_EQ(part_1_b_mod_size, part_1_b_size);
+    EXPECT_EQ(part_2_b_mod_size, part_2_b_size);
+
+    // RESIZE B USING UPDATE_2.CFG
+    metadata_mod = go(metadata, "b", update_cfg_records, messages);
+    
+    EXPECT_NE(metadata_mod, nullptr);
+
+    part_1_a_mod = FindPartition(*metadata_mod, "part_1_a");
+    part_2_a_mod = FindPartition(*metadata_mod, "part_2_a");
+    part_1_b_mod = FindPartition(*metadata_mod, "part_1_b");
+    part_2_b_mod = FindPartition(*metadata_mod, "part_2_b");
+    part_1_a_mod_size = GetPartitionSize(*metadata_mod, *part_1_a_mod);
+    part_2_a_mod_size = GetPartitionSize(*metadata_mod, *part_2_a_mod);
+    part_1_b_mod_size = GetPartitionSize(*metadata_mod, *part_1_b_mod);
+    part_2_b_mod_size = GetPartitionSize(*metadata_mod, *part_2_b_mod);
+
+    EXPECT_EQ(part_1_a_mod_size, part_1_a_size);
+    EXPECT_EQ(part_2_a_mod_size, part_2_a_size);
+    EXPECT_EQ(part_1_b_mod_size, update_cfg_records[0].new_size);
+    EXPECT_EQ(part_2_b_mod_size, update_cfg_records[1].new_size);
+}
+
+// Try to read resize partitions. Two slots, two partitions each
+// part_1_a == part_2_a == part_2_b
+// part_1_b > part_2_b
+TEST(ResizeDynpartsTest, CopyPartitionsTwoSlotsTwoPartitionsEachPart2ALarger) {
+    ostringstream messages;
+    vector<update_cfg_record> update_cfg_records;
+    android::fs_mgr::LpMetadata metadata;
+    // super_resize_4.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  30 79 223 227 79 129 195 114 38 172 58 25 164 135 53 117 193 160 60 253 219 159 139 165 95 252 225 80 7 215 105 107 148 134 86 140 186 96 133 164 1 41 97 244 212 245 155 237 153 44 104 209 233 128 69 19 71 84 131 32 35 171 91 165 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 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 2048 0 2048 0  2048 0 4096 0  4096 0 6144 0  2048 0 10240 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 10977280 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 10977280 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";
+    metadataio::readMetadata(metadata, metadata_str);
+
+    auto part_1_a = FindPartition(metadata, "part_1_a");
+    auto part_2_a = FindPartition(metadata, "part_2_a");
+    auto part_1_b = FindPartition(metadata, "part_1_b");
+    auto part_2_b = FindPartition(metadata, "part_2_b");
+    auto part_1_a_size = GetPartitionSize(metadata, *part_1_a);
+    auto part_2_a_size = GetPartitionSize(metadata, *part_2_a);
+    auto part_1_b_size = GetPartitionSize(metadata, *part_1_b);
+    auto part_2_b_size = GetPartitionSize(metadata, *part_2_b);
+    EXPECT_EQ(part_1_a_size, 1024*1024); // should be 1 MiB
+    EXPECT_EQ(part_2_a_size, 1024*1024); // should be 1 MiB
+    EXPECT_EQ(part_1_b_size, 2*1024*1024); // should be 2 MiB
+    EXPECT_EQ(part_2_b_size, 1024*1024); // should be 1 MiB
+    EXPECT_EQ(part_1_a_size, part_2_a_size); // should be equal
+    EXPECT_EQ(part_1_a_size, part_2_b_size); // should be equal
+    EXPECT_NE(part_1_a_size, part_1_b_size); // should not be equal
+    EXPECT_NE(part_1_b_size, part_2_b_size); // should not be equal
+    
+    // COPY FROM A TO B
+    auto metadata_mod = go(metadata, "b", update_cfg_records, messages);
+    
+    EXPECT_NE(metadata_mod, nullptr);
+
+    auto part_1_a_mod = FindPartition(*metadata_mod, "part_1_a");
+    auto part_2_a_mod = FindPartition(*metadata_mod, "part_2_a");
+    auto part_1_b_mod = FindPartition(*metadata_mod, "part_1_b");
+    auto part_2_b_mod = FindPartition(*metadata_mod, "part_2_b");
+    auto part_1_a_mod_size = GetPartitionSize(*metadata_mod, *part_1_a_mod);
+    auto part_2_a_mod_size = GetPartitionSize(*metadata_mod, *part_2_a_mod);
+    auto part_1_b_mod_size = GetPartitionSize(*metadata_mod, *part_1_b_mod);
+    auto part_2_b_mod_size = GetPartitionSize(*metadata_mod, *part_2_b_mod);
+
+    EXPECT_EQ(part_1_a_mod_size, part_1_a_size);
+    EXPECT_EQ(part_2_a_mod_size, part_2_a_size);
+    EXPECT_EQ(part_1_b_mod_size, part_1_a_size);
+    EXPECT_EQ(part_2_b_mod_size, part_2_a_size);
+    EXPECT_EQ(part_1_a_mod_size, part_1_b_mod_size);
+    EXPECT_EQ(part_2_a_mod_size, part_2_b_mod_size);
+    EXPECT_EQ(part_1_a_mod_size, part_2_a_mod_size);
+    EXPECT_EQ(part_1_b_mod_size, part_2_b_mod_size);
+
+    // COPY FROM B TO A
+    metadata_mod = go(metadata, "a", update_cfg_records, messages);
+    
+    EXPECT_NE(metadata_mod, nullptr);
+
+    part_1_a_mod = FindPartition(*metadata_mod, "part_1_a");
+    part_2_a_mod = FindPartition(*metadata_mod, "part_2_a");
+    part_1_b_mod = FindPartition(*metadata_mod, "part_1_b");
+    part_2_b_mod = FindPartition(*metadata_mod, "part_2_b");
+    part_1_a_mod_size = GetPartitionSize(*metadata_mod, *part_1_a_mod);
+    part_2_a_mod_size = GetPartitionSize(*metadata_mod, *part_2_a_mod);
+    part_1_b_mod_size = GetPartitionSize(*metadata_mod, *part_1_b_mod);
+    part_2_b_mod_size = GetPartitionSize(*metadata_mod, *part_2_b_mod);
+
+    EXPECT_EQ(part_1_a_mod_size, part_1_b_size);
+    EXPECT_EQ(part_2_a_mod_size, part_2_b_size);
+    EXPECT_EQ(part_1_b_mod_size, part_1_b_size);
+    EXPECT_EQ(part_2_b_mod_size, part_2_b_size);
+    EXPECT_EQ(part_1_a_mod_size, part_1_b_mod_size);
+    EXPECT_EQ(part_2_a_mod_size, part_2_b_mod_size);
+    EXPECT_NE(part_1_a_mod_size, part_2_a_mod_size);
+    EXPECT_NE(part_1_b_mod_size, part_2_b_mod_size);
+}
+
+// Try to read resize partitions. Two slots, two partitions each
+// part_1_a == part_2_a == part_1_b
+// part_1_b < part_2_b
+TEST(ResizeDynpartsTest, CopyPartitionsTwoSlotsTwoPartitionsEachPart2BLarger) {
+    ostringstream messages;
+    vector<update_cfg_record> update_cfg_records;
+    android::fs_mgr::LpMetadata metadata;
+    // super_resize_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 512 0 0 4 52  208 4 24  304 3 48  448 1 64  213 108 82 222 77 86 149 203 28 166 57 83 159 167 173 53 90 79 61 85 239 57 244 197 30 146 155 136 79 37 162 220 86 143 89 202 150 107 47 44 3 71 217 77 137 73 98 190 189 48 98 81 185 130 76 55 203 90 207 194 219 40 185 43 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 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 2048 0 2048 0  2048 0 4096 0  2048 0 6144 0  4096 0 8192 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 10977280 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 10977280 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";
+    metadataio::readMetadata(metadata, metadata_str);
+
+    auto part_1_a = FindPartition(metadata, "part_1_a");
+    auto part_2_a = FindPartition(metadata, "part_2_a");
+    auto part_1_b = FindPartition(metadata, "part_1_b");
+    auto part_2_b = FindPartition(metadata, "part_2_b");
+    auto part_1_a_size = GetPartitionSize(metadata, *part_1_a);
+    auto part_2_a_size = GetPartitionSize(metadata, *part_2_a);
+    auto part_1_b_size = GetPartitionSize(metadata, *part_1_b);
+    auto part_2_b_size = GetPartitionSize(metadata, *part_2_b);
+    EXPECT_EQ(part_1_a_size, 1024*1024); // should be 1 MiB
+    EXPECT_EQ(part_2_a_size, 1024*1024); // should be 1 MiB
+    EXPECT_EQ(part_1_b_size, 1024*1024); // should be 1 MiB
+    EXPECT_EQ(part_2_b_size, 2*1024*1024); // should be 2 MiB
+    EXPECT_EQ(part_1_a_size, part_2_a_size); // should be equal
+    EXPECT_EQ(part_1_a_size, part_1_b_size); // should be equal
+    EXPECT_NE(part_1_a_size, part_2_b_size); // should not be equal
+    EXPECT_NE(part_1_b_size, part_2_b_size); // should not be equal
+    
+    // COPY FROM A TO B
+    auto metadata_mod = go(metadata, "b", update_cfg_records, messages);
+    
+    EXPECT_NE(metadata_mod, nullptr);
+
+    auto part_1_a_mod = FindPartition(*metadata_mod, "part_1_a");
+    auto part_2_a_mod = FindPartition(*metadata_mod, "part_2_a");
+    auto part_1_b_mod = FindPartition(*metadata_mod, "part_1_b");
+    auto part_2_b_mod = FindPartition(*metadata_mod, "part_2_b");
+    auto part_1_a_mod_size = GetPartitionSize(*metadata_mod, *part_1_a_mod);
+    auto part_2_a_mod_size = GetPartitionSize(*metadata_mod, *part_2_a_mod);
+    auto part_1_b_mod_size = GetPartitionSize(*metadata_mod, *part_1_b_mod);
+    auto part_2_b_mod_size = GetPartitionSize(*metadata_mod, *part_2_b_mod);
+
+    EXPECT_EQ(part_1_a_mod_size, part_1_a_size);
+    EXPECT_EQ(part_2_a_mod_size, part_2_a_size);
+    EXPECT_EQ(part_1_b_mod_size, part_1_a_size);
+    EXPECT_EQ(part_2_b_mod_size, part_2_a_size);
+    EXPECT_EQ(part_1_a_mod_size, part_1_b_mod_size);
+    EXPECT_EQ(part_2_a_mod_size, part_2_b_mod_size);
+    EXPECT_EQ(part_1_a_mod_size, part_2_a_mod_size);
+    EXPECT_EQ(part_1_b_mod_size, part_2_b_mod_size);
+
+    // COPY FROM B TO A
+    metadata_mod = go(metadata, "a", update_cfg_records, messages);
+    
+    EXPECT_NE(metadata_mod, nullptr);
+
+    part_1_a_mod = FindPartition(*metadata_mod, "part_1_a");
+    part_2_a_mod = FindPartition(*metadata_mod, "part_2_a");
+    part_1_b_mod = FindPartition(*metadata_mod, "part_1_b");
+    part_2_b_mod = FindPartition(*metadata_mod, "part_2_b");
+    part_1_a_mod_size = GetPartitionSize(*metadata_mod, *part_1_a_mod);
+    part_2_a_mod_size = GetPartitionSize(*metadata_mod, *part_2_a_mod);
+    part_1_b_mod_size = GetPartitionSize(*metadata_mod, *part_1_b_mod);
+    part_2_b_mod_size = GetPartitionSize(*metadata_mod, *part_2_b_mod);
+
+    EXPECT_EQ(part_1_a_mod_size, part_1_b_size);
+    EXPECT_EQ(part_2_a_mod_size, part_2_b_size);
+    EXPECT_EQ(part_1_b_mod_size, part_1_b_size);
+    EXPECT_EQ(part_2_b_mod_size, part_2_b_size);
+    EXPECT_EQ(part_1_a_mod_size, part_1_b_mod_size);
+    EXPECT_EQ(part_2_a_mod_size, part_2_b_mod_size);
+    EXPECT_NE(part_1_a_mod_size, part_2_a_mod_size);
+    EXPECT_NE(part_1_b_mod_size, part_2_b_mod_size);
+}
+
+// Try to read resize partitions. Two slots, two partitions each
+// part_1_a == part_2_a
+// part_2_a == part_2_b
+// part_1_a < part_1_b
+TEST(ResizeDynpartsTest, CopyPartitionsTwoSlotsTwoPartitionsEachPartsAEqualPartsBEqualPartsBLargerThanPartsA) {
+    ostringstream messages;
+    vector<update_cfg_record> update_cfg_records;
+    android::fs_mgr::LpMetadata metadata;
+    // super_resize_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  111 5 193 182 144 222 140 80 76 116 73 97 185 171 31 49 21 106 35 62 190 124 21 97 211 75 10 151 139 170 255 218 252 47 133 228 30 243 49 72 55 140 234 20 13 134 170 135 249 89 31 45 21 123 47 234 142 251 163 197 147 98 116 104 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 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 2048 0 2048 0  2048 0 4096 0  4096 0 6144 0  4096 0 10240 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 10977280 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 10977280 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";
+    metadataio::readMetadata(metadata, metadata_str);
+
+    auto part_1_a = FindPartition(metadata, "part_1_a");
+    auto part_2_a = FindPartition(metadata, "part_2_a");
+    auto part_1_b = FindPartition(metadata, "part_1_b");
+    auto part_2_b = FindPartition(metadata, "part_2_b");
+    auto part_1_a_size = GetPartitionSize(metadata, *part_1_a);
+    auto part_2_a_size = GetPartitionSize(metadata, *part_2_a);
+    auto part_1_b_size = GetPartitionSize(metadata, *part_1_b);
+    auto part_2_b_size = GetPartitionSize(metadata, *part_2_b);
+    EXPECT_EQ(part_1_a_size, 1024*1024); // should be 1 MiB
+    EXPECT_EQ(part_2_a_size, 1024*1024); // should be 1 MiB
+    EXPECT_EQ(part_1_b_size, 2*1024*1024); // should be 2 MiB
+    EXPECT_EQ(part_2_b_size, 2*1024*1024); // should be 2 MiB
+    EXPECT_EQ(part_1_a_size, part_2_a_size); // should be equal
+    EXPECT_EQ(part_1_b_size, part_2_b_size); // should be equal
+    EXPECT_NE(part_1_a_size, part_1_b_size); // should not be equal
+    
+    // COPY FROM A TO B
+    auto metadata_mod = go(metadata, "b", update_cfg_records, messages);
+    
+    EXPECT_NE(metadata_mod, nullptr);
+
+    auto part_1_a_mod = FindPartition(*metadata_mod, "part_1_a");
+    auto part_2_a_mod = FindPartition(*metadata_mod, "part_2_a");
+    auto part_1_b_mod = FindPartition(*metadata_mod, "part_1_b");
+    auto part_2_b_mod = FindPartition(*metadata_mod, "part_2_b");
+    auto part_1_a_mod_size = GetPartitionSize(*metadata_mod, *part_1_a_mod);
+    auto part_2_a_mod_size = GetPartitionSize(*metadata_mod, *part_2_a_mod);
+    auto part_1_b_mod_size = GetPartitionSize(*metadata_mod, *part_1_b_mod);
+    auto part_2_b_mod_size = GetPartitionSize(*metadata_mod, *part_2_b_mod);
+
+    EXPECT_EQ(part_1_a_mod_size, part_1_a_size);
+    EXPECT_EQ(part_2_a_mod_size, part_2_a_size);
+    EXPECT_EQ(part_1_b_mod_size, part_1_a_size);
+    EXPECT_EQ(part_2_b_mod_size, part_2_a_size);
+    EXPECT_EQ(part_1_a_mod_size, part_1_b_mod_size);
+    EXPECT_EQ(part_2_a_mod_size, part_2_b_mod_size);
+    EXPECT_EQ(part_1_a_mod_size, part_2_a_mod_size);
+    EXPECT_EQ(part_1_b_mod_size, part_2_b_mod_size);
+
+    // COPY FROM B TO A
+    metadata_mod = go(metadata, "a", update_cfg_records, messages);
+    
+    EXPECT_NE(metadata_mod, nullptr);
+
+    part_1_a_mod = FindPartition(*metadata_mod, "part_1_a");
+    part_2_a_mod = FindPartition(*metadata_mod, "part_2_a");
+    part_1_b_mod = FindPartition(*metadata_mod, "part_1_b");
+    part_2_b_mod = FindPartition(*metadata_mod, "part_2_b");
+    part_1_a_mod_size = GetPartitionSize(*metadata_mod, *part_1_a_mod);
+    part_2_a_mod_size = GetPartitionSize(*metadata_mod, *part_2_a_mod);
+    part_1_b_mod_size = GetPartitionSize(*metadata_mod, *part_1_b_mod);
+    part_2_b_mod_size = GetPartitionSize(*metadata_mod, *part_2_b_mod);
+
+    EXPECT_EQ(part_1_a_mod_size, part_1_b_size);
+    EXPECT_EQ(part_2_a_mod_size, part_2_b_size);
+    EXPECT_EQ(part_1_b_mod_size, part_1_b_size);
+    EXPECT_EQ(part_2_b_mod_size, part_2_b_size);
+    EXPECT_EQ(part_1_a_mod_size, part_1_b_mod_size);
+    EXPECT_EQ(part_2_a_mod_size, part_2_b_mod_size);
+    EXPECT_EQ(part_1_a_mod_size, part_2_a_mod_size);
+    EXPECT_EQ(part_1_b_mod_size, part_2_b_mod_size);
+}
diff --git a/src/dynamic-partitions/resize-dynparts/test/update_1.cfg b/src/dynamic-partitions/resize-dynparts/test/update_1.cfg
new file mode 100644 (file)
index 0000000..67e8f65
--- /dev/null
@@ -0,0 +1 @@
+part_1 part_image_name upgrade_type part_dev 0 1048576 2097152 old_hash new_hash
\ No newline at end of file
diff --git a/src/dynamic-partitions/resize-dynparts/test/update_2.cfg b/src/dynamic-partitions/resize-dynparts/test/update_2.cfg
new file mode 100644 (file)
index 0000000..dd473e8
--- /dev/null
@@ -0,0 +1,2 @@
+part_1 part_image_name upgrade_type part_dev 0 1048576 2097152 old_hash new_hash
+part_2 part_image_name upgrade_type part_dev 0 1048576 3145728 old_hash new_hash
\ No newline at end of file
diff --git a/src/dynamic-partitions/resize-dynparts/test/update_artificial.cfg b/src/dynamic-partitions/resize-dynparts/test/update_artificial.cfg
new file mode 100644 (file)
index 0000000..29b8f38
--- /dev/null
@@ -0,0 +1,6 @@
+part_name_0 part_image_name_0 upgrade_type_0 part_dev_0 0  8 128 old_hash_0 new_hash_0
+part_name_1 part_image_name_1 upgrade_type_1 part_dev_1 1 16 256 old_hash_1 new_hash_1
+part_name_2 part_image_name_2 upgrade_type_2 part_dev_2 2 24 384 old_hash_2 new_hash_2
+part_name_3 part_image_name_3 upgrade_type_3 part_dev_3 3 32 512 old_hash_3 new_hash_3
+part_name_4 part_image_name_4 upgrade_type_4 part_dev_4 4 40 640 old_hash_4 new_hash_4
+part_name_5 part_image_name_5 upgrade_type_5 part_dev_5 5 48 768 old_hash_5 new_hash_5
diff --git a/src/dynamic-partitions/resize-dynparts/test/update_empty.cfg b/src/dynamic-partitions/resize-dynparts/test/update_empty.cfg
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/dynamic-partitions/resize-dynparts/test/update_malformed.cfg b/src/dynamic-partitions/resize-dynparts/test/update_malformed.cfg
new file mode 100644 (file)
index 0000000..8f6d1ea
--- /dev/null
@@ -0,0 +1,2 @@
+this is a file which
+does not have a format of update.cfg
\ No newline at end of file
diff --git a/src/dynamic-partitions/resize-dynparts/test/update_real.cfg b/src/dynamic-partitions/resize-dynparts/test/update_real.cfg
new file mode 100644 (file)
index 0000000..00231e7
--- /dev/null
@@ -0,0 +1,6 @@
+BOOT           boot.img                FULL_IMAGE:BEFORE_BOOT_FOTA             /dev/mmcblk0p1          0       67108864                67108864                888f83a2d8ed4ce1a546bcad59f10cd73426a83a                64fa0e0dfb78609b314e8d050fd7f138f26ef2a4
+hal            hal.img         DELTA_IMAGE:AT_BOOT_FOTA                /dev/mmcblk0p10         0       107216896               107216896               f5b68d272f8a7849d64d6ec9c95052578d969d5a                c7bb4ae65822cf1d7c16342d58241a32e720677a
+modules                modules.img             FULL_IMAGE:BEFORE_BOOT_FOTA             /dev/mmcblk0p6          0       20971520                20971520                094f2d2539d010c7cf379c78e1893b715b2c5446                6beb947291abcba23e828631a45f0d854116bb17
+rootfs         rootfs.img              DELTA_IMAGE:AT_BOOT_FOTA                /dev/mmcblk0p2          0       627478528               627486720               bf0ca2d9927c64531ca7899ee88202d09a57a9e8                cb2f79550766198597baaff5127c97a274697211
+ramdisk                ramdisk.img             FULL_IMAGE:AT_BOOT_FOTA         /dev/mmcblk0p7          0       22902784                22902784                3ed11fab9a4845faafecea55a6dd4c8cdb5f3d57                faa0aabc206c65bb42eeb1e453222347228658a6
+ramdisk-recovery               ramdisk-recovery.img            FULL_IMAGE:BEFORE_BOOT_FOTA             /dev/mmcblk0p8          0       26178560                26178560                67386cad4b5773240730134421ea8b91f57e2d15                52bb203644adabb9fa3f8386159b8dcd91372971
diff --git a/src/dynamic-partitions/testlib/CMakeLists.txt b/src/dynamic-partitions/testlib/CMakeLists.txt
new file mode 100644 (file)
index 0000000..8bd5bca
--- /dev/null
@@ -0,0 +1,6 @@
+set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD_REQUIRED True)
+
+add_executable(super_dump super_dump.cpp metadataio.cpp)
+target_compile_options(super_dump PRIVATE -Wall -Wextra -pedantic -fPIE -Wno-stringop-truncation)
+target_link_libraries(super_dump PRIVATE lp)
\ No newline at end of file
diff --git a/src/dynamic-partitions/testlib/generate_test_data.sh b/src/dynamic-partitions/testlib/generate_test_data.sh
new file mode 100755 (executable)
index 0000000..8db42bd
--- /dev/null
@@ -0,0 +1,338 @@
+ #!/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
+
+    super_size_mb=6 # MB
+    super_size="$(($super_size_mb*$MIB))"
+    group_size="$(($super_size-$metadata_aligned_size))"
+    part_size="$group_size" # 5MB
+
+    lpmake -F --device-size=$super_size \
+           --metadata-size=$metadata_size \
+           --metadata-slots=$metadata_slots \
+           -o=super_1.img \
+           -g "group_a:$group_size" \
+           -p "part_1_a:none:$part_size:group_a"
+}
+
+# super with one slot, two partitions
+create_super_2 () {
+    metadata_slots=1
+    metadata_size=65536
+    metadata_aligned_size="$(get_aligned_size $metadata_size)" #should be 1 MB
+
+    super_size_mb=11 # MB
+    super_size="$(($super_size_mb*$MIB))"
+    group_size="$(($super_size-$metadata_aligned_size))"
+    part_size="$(($group_size/2))" # 5MB
+
+    lpmake -F --device-size=$super_size \
+           --metadata-size=$metadata_size \
+           --metadata-slots=$metadata_slots \
+           -o=super_2.img \
+           -g "group_a:$group_size" \
+           -p "part_1_a:none:$part_size:group_a" \
+           -p "part_2_a:none:$part_size:group_a"
+}
+
+# super with one slot, three partitions
+# super has 31 MB becuase without metadata there is 60 MB for partitions which nicely divides by 3 and by 6
+create_super_3 () {
+    metadata_slots=1
+    metadata_size=65536
+    metadata_aligned_size="$(get_aligned_size $metadata_size)" #should be 1 MB
+
+    super_size_mb=16 # MB
+    super_size="$(($super_size_mb*$MIB))"
+    group_size="$(($super_size-$metadata_aligned_size))"
+    part_size="$(($group_size/3))" # 5MB
+
+    lpmake -F --device-size=$super_size \
+           --metadata-size=$metadata_size \
+           --metadata-slots=$metadata_slots \
+           -o=super_3.img \
+           -g "group_a:$group_size" \
+           -p "part_1_a:none:$part_size:group_a" \
+           -p "part_2_a:none:$part_size:group_a" \
+           -p "part_3_a:none:$part_size:group_a"
+}
+
+# super with one slot, four partitions
+create_super_4 () {
+    metadata_slots=1
+    metadata_size=65536
+    metadata_aligned_size="$(get_aligned_size $metadata_size)" #should be 1 MB
+
+    super_size_mb=21 # MB
+    super_size="$(($super_size_mb*$MIB))"
+    group_size="$(($super_size-$metadata_aligned_size))"
+    part_size="$(($group_size/4))" # 5MB
+
+    lpmake -F --device-size=$super_size \
+           --metadata-size=$metadata_size \
+           --metadata-slots=$metadata_slots \
+           -o=super_4.img \
+           -g "group_a:$group_size" \
+           -p "part_1_a:none:$part_size:group_a" \
+           -p "part_2_a:none:$part_size:group_a" \
+           -p "part_3_a:none:$part_size:group_a" \
+           -p "part_4_a:none:$part_size:group_a"
+}
+
+# super with two slots, one partition each
+create_super_5 () {
+    metadata_slots=2
+    metadata_size=65536
+    metadata_aligned_size="$(get_aligned_size $metadata_size)" #should be 1 MB
+
+    super_size_mb=11 # MB
+    super_size="$(($super_size_mb*$MIB))"
+    group_size="$((($super_size-$metadata_aligned_size)/2))"
+    part_size="$group_size" # 5MB
+
+    lpmake -F --device-size=$super_size \
+           --metadata-size=$metadata_size \
+           --metadata-slots=$metadata_slots \
+           -o=super_5.img \
+           -g "group_a:$group_size" \
+           -p "part_1_a:none:$part_size:group_a" \
+           -g "group_b:$group_size" \
+           -p "part_1_b:none:$part_size:group_b"
+}
+
+# super with two slots, two partitions each
+create_super_6 () {
+    metadata_slots=2
+    metadata_size=65536
+    metadata_aligned_size="$(get_aligned_size $metadata_size)" #should be 1 MB
+
+    super_size_mb=21 # MB
+    super_size="$(($super_size_mb*$MIB))"
+    group_size="$((($super_size-$metadata_aligned_size)/2))"
+    part_size="$(($group_size/2))" # 5MB
+
+    lpmake -F --device-size=$super_size \
+           --metadata-size=$metadata_size \
+           --metadata-slots=$metadata_slots \
+           -o=super_6.img \
+           -g "group_a:$group_size" \
+           -p "part_1_a:none:$part_size:group_a" \
+           -p "part_2_a:none:$part_size:group_a" \
+           -g "group_b:$group_size" \
+           -p "part_1_b:none:$part_size:group_b" \
+           -p "part_2_b:none:$part_size:group_b"
+}
+
+# super with two slots, three partitions each
+create_super_7 () {
+    metadata_slots=2
+    metadata_size=65536
+    metadata_aligned_size="$(get_aligned_size $metadata_size)" #should be 1 MB
+
+    super_size_mb=31 # MB
+    super_size="$(($super_size_mb*$MIB))"
+    group_size="$((($super_size-$metadata_aligned_size)/2))"
+    part_size="$(($group_size/3))" # 5MB
+
+    lpmake -F --device-size=$super_size \
+           --metadata-size=$metadata_size \
+           --metadata-slots=$metadata_slots \
+           -o=super_7.img \
+           -g "group_a:$group_size" \
+           -p "part_1_a:none:$part_size:group_a" \
+           -p "part_2_a:none:$part_size:group_a" \
+           -p "part_3_a:none:$part_size:group_a" \
+           -g "group_b:$group_size" \
+           -p "part_1_b:none:$part_size:group_b" \
+           -p "part_2_b:none:$part_size:group_b" \
+           -p "part_3_b:none:$part_size:group_b"
+}
+
+# super with two slots, four partitions each
+create_super_8 () {
+    metadata_slots=2
+    metadata_size=65536
+    metadata_aligned_size="$(get_aligned_size $metadata_size)" #should be 1 MB
+
+    super_size_mb=41 # MB
+    super_size="$(($super_size_mb*$MIB))"
+    group_size="$((($super_size-$metadata_aligned_size)/2))"
+    part_size="$(($group_size/4))" # 10 MB
+
+    lpmake -F --device-size=$super_size \
+           --metadata-size=$metadata_size \
+           --metadata-slots=$metadata_slots \
+           -o=super_8.img \
+           -g "group_a:$group_size" \
+           -p "part_1_a:none:$part_size:group_a" \
+           -p "part_2_a:none:$part_size:group_a" \
+           -p "part_3_a:none:$part_size:group_a" \
+           -p "part_4_a:none:$part_size:group_a" \
+           -g "group_b:$group_size" \
+           -p "part_1_b:none:$part_size:group_b" \
+           -p "part_2_b:none:$part_size:group_b" \
+           -p "part_3_b:none:$part_size:group_b" \
+           -p "part_4_b:none:$part_size:group_b"
+}
+
+create_super_1
+create_super_2
+create_super_3
+create_super_4
+create_super_5
+create_super_6
+create_super_7
+create_super_8
+
+one_mib_part_size_blocks="$(($MIB/512))"
+two_mib_part_size_blocks="$((2*$MIB/512))"
+three_mib_part_size_blocks="$((3*$MIB/512))"
+four_mib_part_size_blocks="$((4*$MIB/512))"
+five_mib_part_size_blocks="$((5*$MIB/512))"
+ten_mib_part_size_blocks="$((10*$MIB/512))"
+
+dd if=/dev/null of=part_one_mib.img seek="$one_mib_part_size_blocks"
+dd if=/dev/null of=part_two_mib.img seek="$two_mib_part_size_blocks"
+dd if=/dev/null of=part_three_mib.img seek="$three_mib_part_size_blocks"
+dd if=/dev/null of=part_four_mib.img seek="$four_mib_part_size_blocks"
+dd if=/dev/null of=part_five_mib.img seek="$five_mib_part_size_blocks"
+dd if=/dev/null of=part_ten_mib.img seek="$ten_mib_part_size_blocks"
+
+cp super_3.img super_partitioned_1.img
+
+lpadd super_partitioned_1.img part_1_a group_a part_one_mib.img --replace
+lpadd super_partitioned_1.img part_3_a group_a part_one_mib.img --replace
+lpadd super_partitioned_1.img part_4_a group_a part_five_mib.img
+
+cp super_3.img super_partitioned_2.img
+
+lpadd super_partitioned_2.img part_1_a group_a part_one_mib.img --replace
+lpadd super_partitioned_2.img part_3_a group_a part_one_mib.img --replace
+lpadd super_partitioned_2.img part_4_a group_a part_five_mib.img
+lpadd super_partitioned_2.img part_2_a group_a part_one_mib.img --replace
+lpadd super_partitioned_2.img part_5_a group_a part_five_mib.img
+
+cp super_4.img super_partitioned_3.img
+
+lpadd super_partitioned_3.img part_3_a group_a part_one_mib.img --replace
+lpadd super_partitioned_3.img part_2_a group_a part_one_mib.img --replace
+lpadd super_partitioned_3.img part_1_a group_a part_one_mib.img --replace
+lpadd super_partitioned_3.img part_5_a group_a part_ten_mib.img
+
+cp super_7.img super_partitioned_4.img
+
+lpadd super_partitioned_4.img part_1_a group_a part_one_mib.img --replace
+lpadd super_partitioned_4.img part_3_a group_a part_one_mib.img --replace
+lpadd super_partitioned_4.img part_4_a group_a part_five_mib.img
+
+cp super_7.img super_partitioned_5.img
+
+lpadd super_partitioned_5.img part_1_b group_b part_one_mib.img --replace
+lpadd super_partitioned_5.img part_3_b group_b part_one_mib.img --replace
+lpadd super_partitioned_5.img part_4_b group_b part_five_mib.img
+
+cp super_7.img super_partitioned_6.img
+
+lpadd super_partitioned_6.img part_1_a group_a part_one_mib.img --replace
+lpadd super_partitioned_6.img part_3_a group_a part_one_mib.img --replace
+lpadd super_partitioned_6.img part_4_a group_a part_five_mib.img
+
+lpadd super_partitioned_6.img part_1_b group_b part_one_mib.img --replace
+lpadd super_partitioned_6.img part_3_b group_b part_one_mib.img --replace
+lpadd super_partitioned_6.img part_4_b group_b part_five_mib.img
+
+cp super_8.img super_partitioned_7.img
+
+lpadd super_partitioned_7.img part_3_a group_a part_one_mib.img --replace
+lpadd super_partitioned_7.img part_2_a group_a part_one_mib.img --replace
+lpadd super_partitioned_7.img part_1_a group_a part_one_mib.img --replace
+lpadd super_partitioned_7.img part_5_a group_a part_ten_mib.img
+
+cp super_8.img super_partitioned_8.img
+
+lpadd super_partitioned_8.img part_3_b group_b part_one_mib.img --replace
+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
+lpadd super_partitioned_9.img part_2_a group_a part_one_mib.img --replace
+lpadd super_partitioned_9.img part_1_a group_a part_one_mib.img --replace
+lpadd super_partitioned_9.img part_5_a group_a part_ten_mib.img
+
+lpadd super_partitioned_9.img part_3_b group_b part_one_mib.img --replace
+lpadd super_partitioned_9.img part_2_b group_b part_one_mib.img --replace
+lpadd super_partitioned_9.img part_1_b group_b part_one_mib.img --replace
+lpadd super_partitioned_9.img part_5_b group_b part_ten_mib.img
+
+# super_dump is a simple tool which reads metadata
+# from an image and prints it to stdin using metadataio.h
+super_dump super_1.img > super_1.in
+super_dump super_2.img > super_2.in
+super_dump super_3.img > super_3.in
+super_dump super_4.img > super_4.in
+super_dump super_5.img > super_5.in
+super_dump super_6.img > super_6.in
+super_dump super_7.img > super_7.in
+super_dump super_8.img > super_8.in
+super_dump super_partitioned_1.img > super_partitioned_1.in
+super_dump super_partitioned_2.img > super_partitioned_2.in
+super_dump super_partitioned_3.img > super_partitioned_3.in
+super_dump super_partitioned_4.img > super_partitioned_4.in
+super_dump super_partitioned_5.img > super_partitioned_5.in
+super_dump super_partitioned_6.img > super_partitioned_6.in
+super_dump super_partitioned_7.img > super_partitioned_7.in
+super_dump super_partitioned_8.img > super_partitioned_8.in
+super_dump super_partitioned_9.img > super_partitioned_9.in
+
+parse-dynparts super_1.img > super_1.out
+parse-dynparts super_2.img > super_2.out
+parse-dynparts super_3.img > super_3.out
+parse-dynparts super_4.img > super_4.out
+parse-dynparts super_5.img > super_5.out
+parse-dynparts super_6.img > super_6.out
+parse-dynparts super_7.img > super_7.out
+parse-dynparts super_8.img > super_8.out
+parse-dynparts super_partitioned_1.img > super_partitioned_1.out
+parse-dynparts super_partitioned_2.img > super_partitioned_2.out
+parse-dynparts super_partitioned_3.img > super_partitioned_3.out
+parse-dynparts super_partitioned_4.img > super_partitioned_4.out
+parse-dynparts super_partitioned_5.img > super_partitioned_5.out
+parse-dynparts super_partitioned_6.img > super_partitioned_6.out
+parse-dynparts super_partitioned_7.img > super_partitioned_7.out
+parse-dynparts super_partitioned_8.img > super_partitioned_8.out
+parse-dynparts super_partitioned_9.img > super_partitioned_9.out
+
+parse-dynparts super_1.img --list-tables > super_1_list.out
+parse-dynparts super_2.img --list-tables > super_2_list.out
+parse-dynparts super_3.img --list-tables > super_3_list.out
+parse-dynparts super_4.img --list-tables > super_4_list.out
+parse-dynparts super_5.img --list-tables > super_5_list.out
+parse-dynparts super_6.img --list-tables > super_6_list.out
+parse-dynparts super_7.img --list-tables > super_7_list.out
+parse-dynparts super_8.img --list-tables > super_8_list.out
+parse-dynparts super_partitioned_1.img --list-tables > super_partitioned_1_list.out
+parse-dynparts super_partitioned_2.img --list-tables > super_partitioned_2_list.out
+parse-dynparts super_partitioned_3.img --list-tables > super_partitioned_3_list.out
+parse-dynparts super_partitioned_4.img --list-tables > super_partitioned_4_list.out
+parse-dynparts super_partitioned_5.img --list-tables > super_partitioned_5_list.out
+parse-dynparts super_partitioned_6.img --list-tables > super_partitioned_6_list.out
+parse-dynparts super_partitioned_7.img --list-tables > super_partitioned_7_list.out
+parse-dynparts super_partitioned_8.img --list-tables > super_partitioned_8_list.out
+parse-dynparts super_partitioned_9.img --list-tables > super_partitioned_9_list.out
diff --git a/src/dynamic-partitions/testlib/generate_test_data_for_resizing.sh b/src/dynamic-partitions/testlib/generate_test_data_for_resizing.sh
new file mode 100755 (executable)
index 0000000..f9d489b
--- /dev/null
@@ -0,0 +1,140 @@
+ #!/bin/bash
+
+MIB=1048576 # 1 MiB is this amount of bytes
+TWO_MIBS="$((2*MIB))" # 2MB
+
+# super with two slots, one partition each. Both partitions
+# have the same size
+create_super_for_resize_1 () {
+    metadata_slots=2
+    metadata_size=65536
+
+    super_size="$((21*MIB))"
+    group_size="$(((super_size-metadata_size)/2))"
+    
+
+    lpmake -F --device-size=$super_size \
+           --metadata-size=$metadata_size \
+           --metadata-slots=$metadata_slots \
+           -o=super_resize_1.img \
+           -g "group_a:$group_size" \
+           -p "part_1_a:none:$MIB:group_a" \
+           -g "group_b:$group_size" \
+           -p "part_1_b:none:$MIB:group_b"
+}
+
+# super with two slots, one partition each
+# Partitions have different size
+create_super_for_resize_2 () {
+    metadata_slots=2
+    metadata_size=65536
+
+    super_size="$((21*MIB))"
+    group_size="$(((super_size-metadata_size)/2))"
+
+    lpmake -F --device-size=$super_size \
+           --metadata-size=$metadata_size \
+           --metadata-slots=$metadata_slots \
+           -o=super_resize_2.img \
+           -g "group_a:$group_size" \
+           -p "part_1_a:none:$MIB:group_a" \
+           -g "group_b:$group_size" \
+           -p "part_1_b:none:$TWO_MIBS:group_b"
+}
+
+# super with two slots, two partitions each
+create_super_for_resize_3 () {
+    metadata_slots=2
+    metadata_size=65536
+
+    super_size="$((21*MIB))"
+    group_size="$(((super_size-metadata_size)/2))"
+
+    lpmake -F --device-size=$super_size \
+           --metadata-size=$metadata_size \
+           --metadata-slots=$metadata_slots \
+           -o=super_resize_3.img \
+           -g "group_a:$group_size" \
+           -p "part_1_a:none:$MIB:group_a" \
+           -p "part_2_a:none:$MIB:group_a" \
+           -g "group_b:$group_size" \
+           -p "part_1_b:none:$MIB:group_b" \
+           -p "part_2_b:none:$MIB:group_b"
+}
+
+# super with two slots, two partitions each
+create_super_for_resize_4 () {
+    metadata_slots=2
+    metadata_size=65536
+
+    super_size="$((21*MIB))"
+    group_size="$(((super_size-metadata_size)/2))"
+
+    lpmake -F --device-size=$super_size \
+           --metadata-size=$metadata_size \
+           --metadata-slots=$metadata_slots \
+           -o=super_resize_4.img \
+           -g "group_a:$group_size" \
+           -p "part_1_a:none:$MIB:group_a" \
+           -p "part_2_a:none:$MIB:group_a" \
+           -g "group_b:$group_size" \
+           -p "part_1_b:none:$TWO_MIBS:group_b" \
+           -p "part_2_b:none:$MIB:group_b"
+}
+
+# super with two slots, two partitions each
+create_super_for_resize_5 () {
+    metadata_slots=2
+    metadata_size=65536
+
+    super_size="$((21*MIB))"
+    group_size="$(((super_size-metadata_size)/2))"
+
+    lpmake -F --device-size=$super_size \
+           --metadata-size=$metadata_size \
+           --metadata-slots=$metadata_slots \
+           -o=super_resize_5.img \
+           -g "group_a:$group_size" \
+           -p "part_1_a:none:$MIB:group_a" \
+           -p "part_2_a:none:$MIB:group_a" \
+           -g "group_b:$group_size" \
+           -p "part_1_b:none:$MIB:group_b" \
+           -p "part_2_b:none:$TWO_MIBS:group_b"
+}
+
+# super with two slots, two partitions each
+create_super_for_resize_6 () {
+    metadata_slots=2
+    metadata_size=65536
+
+    super_size="$((21*MIB))"
+    group_size="$(((super_size-metadata_size)/2))"
+
+    lpmake -F --device-size=$super_size \
+           --metadata-size=$metadata_size \
+           --metadata-slots=$metadata_slots \
+           -o=super_resize_6.img \
+           -g "group_a:$group_size" \
+           -p "part_1_a:none:$MIB:group_a" \
+           -p "part_2_a:none:$MIB:group_a" \
+           -g "group_b:$group_size" \
+           -p "part_1_b:none:$TWO_MIBS:group_b" \
+           -p "part_2_b:none:$TWO_MIBS:group_b"
+}
+
+
+create_super_for_resize_1
+create_super_for_resize_2
+create_super_for_resize_3
+create_super_for_resize_4
+create_super_for_resize_5
+create_super_for_resize_6
+
+# super_dump is a simple tool which reads metadata
+# from an image and prints it to stdin using metadataio.h
+/home/j.kryszyn/git/tizen/upgrade/build/src/dynamic-partitions/testlib/super_dump super_resize_1.img > super_resize_1.in
+/home/j.kryszyn/git/tizen/upgrade/build/src/dynamic-partitions/testlib/super_dump super_resize_2.img > super_resize_2.in
+/home/j.kryszyn/git/tizen/upgrade/build/src/dynamic-partitions/testlib/super_dump super_resize_3.img > super_resize_3.in
+/home/j.kryszyn/git/tizen/upgrade/build/src/dynamic-partitions/testlib/super_dump super_resize_4.img > super_resize_4.in
+/home/j.kryszyn/git/tizen/upgrade/build/src/dynamic-partitions/testlib/super_dump super_resize_5.img > super_resize_5.in
+/home/j.kryszyn/git/tizen/upgrade/build/src/dynamic-partitions/testlib/super_dump super_resize_6.img > super_resize_6.in
diff --git a/src/dynamic-partitions/testlib/metadataio.cpp b/src/dynamic-partitions/testlib/metadataio.cpp
new file mode 100644 (file)
index 0000000..4414174
--- /dev/null
@@ -0,0 +1,442 @@
+/* Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * SPDX-License-Identifier: MIT */
+
+#include <cstring>
+#include <sstream>
+#include <string>
+
+#include <liblp/liblp.h>
+
+#include "metadataio.h"
+
+using namespace metadataio;
+
+void addPartitions(android::fs_mgr::LpMetadata &metadata, int number_of_groups, int number_of_parts) {
+    LpMetadataPartition p;
+    LpMetadataExtent e;
+
+    for(int group = 0; group < number_of_groups; ++group) {
+        std::string group_suffix =  "_";
+        group_suffix += ('a' + group);
+
+        for(int part = 0; part < number_of_parts; ++part) {
+            int idx = group * number_of_parts + part;
+            p.attributes = 0;
+            p.first_extent_index = idx;
+            p.num_extents = 1;
+            p.group_index = group + 1;
+            std::string part_name = "part_" + std::to_string(part + 1) + group_suffix;
+            strncpy(p.name, part_name.c_str(), 36);
+
+            metadata.partitions.push_back(p);
+
+            e.num_sectors = 10240;
+            e.target_type = 0;
+            // add offset equall to idx * PART_SIZE as a number of 512B sectors (1024 * 1024 / 512)
+            e.target_data = 2048 + idx * PART_SIZE * 2048;
+            e.target_source = 0;
+
+            metadata.extents.push_back(e);
+        }
+    }
+}
+
+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;
+    if (a.alignment_offset != b.alignment_offset) return false;
+    if (a.size != b.size) return false;
+    if (a.flags != b.flags) return false;
+
+    for(int i = 0; i < 36; ++i)
+        if (a.partition_name[i] != b.partition_name[i]) return false;
+
+    return true;
+}
+
+bool metadataio::operator==(const LpMetadataPartitionGroup &a, const LpMetadataPartitionGroup &b) {
+    if (a.flags != b.flags) return false;
+    if (a.maximum_size != b.maximum_size) return false;
+
+    for(int i = 0; i < 36; ++i)
+        if (a.name[i] != b.name[i]) return false;
+
+    return true;
+}
+
+bool metadataio::operator==(const LpMetadataExtent &a, const LpMetadataExtent &b) {
+    if (a.num_sectors != b.num_sectors) return false;
+    if (a.target_type != b.target_type) return false;
+    if (a.target_data != b.target_data) return false;
+    if (a.target_source != b.target_source) return false;
+    return true;
+}
+
+bool metadataio::operator==(const LpMetadataPartition &a, const LpMetadataPartition &b){
+    if (a.attributes != b.attributes) return false;
+    if (a.first_extent_index != b.first_extent_index) return false;
+    if (a.num_extents != b.num_extents) return false;
+    if (a.group_index != b.group_index) return false;
+
+    for(int i = 0; i < 36; ++i)
+        if (a.name[i] != b.name[i]) return false;
+
+    return true;
+}
+
+bool metadataio::operator==(const LpMetadataTableDescriptor &a, const LpMetadataTableDescriptor &b) {
+    if (a.offset != b.offset) return false;
+    if (a.num_entries != b.num_entries) return false;
+    if (a.entry_size != b.entry_size) return false;
+    return true;
+}
+
+bool metadataio::operator==(const LpMetadataGeometry &a, const LpMetadataGeometry &b) {
+    if (a.magic != b.magic) return false;
+    if (a.struct_size != b.struct_size) return false;
+    if (a.metadata_max_size != b.metadata_max_size) return false;
+    if (a.metadata_slot_count != b.metadata_slot_count) return false;
+    if (a.logical_block_size != b.logical_block_size) return false;
+
+    for(int i = 0; i < 32; ++i)
+        if (a.checksum[i] != b.checksum[i]) return false;
+
+    return true;
+}
+
+bool metadataio::operator==(const LpMetadataHeader &a, const LpMetadataHeader &b) {
+    if (a.magic != b.magic) return false;
+    if (a.major_version != b.major_version) return false;
+    if (a.minor_version != b.minor_version) return false;
+    if (a.header_size != b.header_size) return false;
+    if (a.tables_size != b.tables_size) return false;
+    if (!(a.partitions == b.partitions)) return false;
+    if (!(a.extents == b.extents)) return false;
+    if (!(a.groups == b.groups)) return false;
+    if (!(a.block_devices == b.block_devices)) return false;
+    if (a.flags != b.flags) return false;
+
+    for(int i = 0; i < 32; ++i)
+        if (a.header_checksum[i] != b.header_checksum[i]) return false;
+
+    for(int i = 0; i < 32; ++i)
+        if (a.tables_checksum[i] != b.tables_checksum[i]) return false;
+
+    for(int i = 0; i < 124; ++i)
+        if (a.reserved[i] != b.reserved[i]) return false;
+
+    return true;
+}
+
+bool metadataio::operator==(const android::fs_mgr::LpMetadata &a, const android::fs_mgr::LpMetadata &b) {
+    if (!(a.geometry == b.geometry)) return false;
+    if (!(a.header == b.header)) return false;
+
+    if (a.partitions.size() != b.partitions.size()) return false;
+
+    for(size_t i = 0; i < a.partitions.size(); ++i)
+        if (!(a.partitions[i] == b.partitions[i])) return false;
+
+    if (a.extents.size() != b.extents.size()) return false;
+
+    for(size_t i = 0; i < a.extents.size(); ++i)
+        if (!(a.extents[i] == b.extents[i])) return false;
+
+    if (a.groups.size() != b.groups.size()) return false;
+
+    for(size_t i = 0; i < a.groups.size(); ++i)
+        if (!(a.groups[i] == b.groups[i])) return false;
+
+    if (a.block_devices.size() != b.block_devices.size()) return false;
+
+    for(size_t i = 0; i < a.block_devices.size(); ++i)
+        if (!(a.block_devices[i] == b.block_devices[i])) return false;
+
+    return true;
+}
+
+std::ostream& metadataio::operator<<(std::ostream &s, const LpMetadataBlockDevice &m) {
+    s << m.first_logical_sector << " "
+        << m.alignment << " "
+        << m.alignment_offset << " "
+        << m.size << " "
+        << m.flags << " ";
+
+    for(int i = 0; i < 36; ++i)
+        s << static_cast<uint64_t>(m.partition_name[i]) << " ";
+
+    return s;
+}
+
+std::istream& metadataio::operator>>(std::istream &s, LpMetadataBlockDevice &m) {
+    uint64_t first_logical_sector, size, alignment, alignment_offset, flags, tmp;
+
+    s >> first_logical_sector >> alignment >> alignment_offset >> size >> flags;
+
+    for(int i = 0; i < 36; ++i) {
+        s >> tmp;
+        m.partition_name[i] = tmp;
+    }
+
+    m.first_logical_sector = first_logical_sector;
+    m.alignment = alignment;
+    m.alignment_offset = alignment_offset;
+    m.size = size;
+    m.flags = flags;
+
+    return s;
+}
+
+std::ostream& metadataio::operator<<(std::ostream &s, const LpMetadataPartitionGroup &m) {
+    s << m.flags << " "
+        << m.maximum_size << " ";
+
+    for(int i = 0; i < 36; ++i)
+        s << static_cast<uint64_t>(m.name[i]) << " ";
+
+    return s;
+}
+
+std::istream& metadataio::operator>>(std::istream &s, LpMetadataPartitionGroup &m) {
+    uint64_t flags, maximum_size, tmp;
+
+    s >> flags >> maximum_size;
+
+    for(int i = 0; i < 36; ++i) {
+        s >> tmp;
+        m.name[i] = tmp;
+    }
+
+    m.flags = flags;
+    m.maximum_size = maximum_size;
+
+    return s;
+}
+
+std::ostream& metadataio::operator<<(std::ostream &s, const LpMetadataExtent &m) {
+    return s << m.num_sectors << " "
+        << m.target_type << " "
+        << m.target_data << " "
+        << m.target_source << " ";
+}
+
+std::istream& metadataio::operator>>(std::istream &s, LpMetadataExtent &m) {
+    uint64_t num_sectors, target_data, target_type, target_source;
+
+    s >> num_sectors >> target_type >> target_data >> target_source;
+
+    m.num_sectors = num_sectors;
+    m.target_type = target_type;
+    m.target_data = target_data;
+    m.target_source = target_source;
+
+    return s;
+}
+
+std::ostream& metadataio::operator<<(std::ostream &s, const LpMetadataPartition &m) {
+    s << m.attributes << " "
+        << m.first_extent_index << " "
+        << m.num_extents << " "
+        << m.group_index << " ";
+
+    for(int i = 0; i < 36; ++i)
+        s << static_cast<uint64_t>(m.name[i]) << " ";
+
+    return s;
+}
+
+std::istream& metadataio::operator>>(std::istream &s, LpMetadataPartition &m) {
+    uint64_t attributes, first_extent_index, num_extents, group_index, tmp;
+
+    s >> attributes >> first_extent_index >> num_extents >> group_index;
+
+    for(int i = 0; i < 36; ++i) {
+        s >> tmp;
+        m.name[i] = tmp;
+    }
+
+    m.attributes = attributes;
+    m.first_extent_index = first_extent_index;
+    m.num_extents = num_extents;
+    m.group_index = group_index;
+
+    return s;
+}
+
+std::ostream& metadataio::operator<<(std::ostream &s, const LpMetadataTableDescriptor &m) {
+    return s << m.offset << " "
+        << m.num_entries << " "
+        << m.entry_size << " ";
+}
+
+std::istream& metadataio::operator>>(std::istream &s, LpMetadataTableDescriptor &m) {
+    uint64_t offset, num_entries, entry_size;
+
+    s >> offset >> num_entries >> entry_size;
+
+    m.offset = offset;
+    m.num_entries = num_entries;
+    m.entry_size = entry_size;
+
+    return s;
+}
+
+std::ostream& metadataio::operator<<(std::ostream &s, const LpMetadataHeader &m) {
+    s << m.magic << " "
+        << m.major_version << " "
+        << m.minor_version << " "
+        << m.header_size << " "
+        << m.tables_size << " "
+        << m.flags << " "
+        << m.partitions << " "
+        << m.extents << " "
+        << m.groups << " "
+        << m.block_devices << " ";
+
+    for(int i = 0; i < 32; ++i)
+        s << static_cast<uint64_t>(m.header_checksum[i]) << " ";
+
+    for(int i = 0; i < 32; ++i)
+        s << static_cast<uint64_t>(m.tables_checksum[i]) << " ";
+
+    for(int i = 0; i < 124; ++i)
+        s << static_cast<uint64_t>(m.reserved[i]) << " ";
+
+    return s;
+}
+
+std::istream& metadataio::operator>>(std::istream &s, LpMetadataHeader &m) {
+    uint64_t magic, header_size, tables_size, flags, major_version, minor_version, tmp;
+
+    s >> magic >> major_version >> minor_version
+      >> header_size >> tables_size >> flags
+      >> m.partitions >> m.extents >> m.groups
+      >> m.block_devices;
+
+    for(int i = 0; i < 32; ++i) {
+        s >> tmp;
+        m.header_checksum[i] = tmp;
+    }
+
+    for(int i = 0; i < 32; ++i) {
+        s >> tmp;
+        m.tables_checksum[i] = tmp;
+    }
+
+    for(int i = 0; i < 124; ++i) {
+        s >> tmp;
+        m.reserved[i] = tmp;
+    }
+
+    m.magic = magic;
+    m.header_size = header_size;
+    m.tables_size = tables_size;
+    m.flags = flags;
+    m.major_version = major_version;
+    m.minor_version = minor_version;
+
+    return s;
+}
+
+std::ostream& metadataio::operator<<(std::ostream &s, const LpMetadataGeometry &m) {
+    s << m.magic << " "
+        << m.struct_size << " "
+        << m.metadata_max_size << " "
+        << m.metadata_slot_count << " "
+        << m.logical_block_size << " ";
+
+    for(int i = 0; i < 32; ++i)
+        s << static_cast<uint64_t>(m.checksum[i]) << " ";
+
+   return s;
+}
+
+std::istream& metadataio::operator>>(std::istream &s, LpMetadataGeometry &m) {
+    uint64_t magic, struct_size, metadata_max_size, metadata_slot_count, logical_block_size, tmp;
+
+    s >> magic >> struct_size >> metadata_max_size
+      >> metadata_slot_count >> logical_block_size;
+
+    for(int i = 0; i < 32; ++i) {
+        s >> tmp;
+        m.checksum[i] = tmp;
+    }
+
+    m.magic = magic;
+    m.struct_size = struct_size;
+    m.metadata_max_size = metadata_max_size;
+    m.metadata_slot_count = metadata_slot_count;
+    m.logical_block_size = logical_block_size;
+
+   return s;
+}
+
+std::ostream& metadataio::operator<<(std::ostream &s, const android::fs_mgr::LpMetadata &m) {
+    s << m.geometry << " " << m.header << " ";
+
+    s << m.partitions.size() << " ";
+
+    for(auto &el : m.partitions)
+        s << el << " ";
+
+    s << m.extents.size() << " ";
+
+    for(auto &el : m.extents)
+        s << el << " ";
+
+    s << m.groups.size() << " ";
+
+    for(auto &el : m.groups)
+        s << el << " ";
+
+    s << m.block_devices.size() << " ";
+
+    for(auto &el : m.block_devices)
+        s << el << " ";
+
+    return s;
+}
+
+std::istream& metadataio::operator>>(std::istream &s, android::fs_mgr::LpMetadata &m) {
+    uint64_t n_partitions, n_extents, n_groups, n_devices;
+    LpMetadataPartition partition;
+    LpMetadataExtent extent;
+    LpMetadataPartitionGroup group;
+    LpMetadataBlockDevice block_device;
+
+    s >> m.geometry >> m.header >> n_partitions;
+
+    for(uint64_t i = 0; i < n_partitions; ++i) {
+        s >> partition;
+        m.partitions.push_back(partition);
+    }
+
+    s >> n_extents;
+
+    for(uint64_t i = 0; i < n_extents; ++i) {
+        s >> extent;
+        m.extents.push_back(extent);
+    }
+
+    s >> n_groups;
+
+    for(uint64_t i = 0; i < n_groups; ++i) {
+        s >> group;
+        m.groups.push_back(group);
+    }
+
+    s >> n_devices;
+
+    for(uint64_t i = 0; i < n_devices; ++i) {
+        s >> block_device;
+        m.block_devices.push_back(block_device);
+    }
+
+    return s;
+}
+
+void metadataio::readMetadata(android::fs_mgr::LpMetadata &m, const std::string &s) {
+    std::stringstream metadata_stream;
+    metadata_stream << s;
+    metadata_stream >> m;
+}
\ No newline at end of file
diff --git a/src/dynamic-partitions/testlib/metadataio.h b/src/dynamic-partitions/testlib/metadataio.h
new file mode 100644 (file)
index 0000000..146f8d2
--- /dev/null
@@ -0,0 +1,42 @@
+/* Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * SPDX-License-Identifier: MIT */
+
+#ifndef _METADATAIO_H_
+#define _METADATAIO_H_
+
+#define PART_SIZE 5 // MB
+
+void addPartitions(android::fs_mgr::LpMetadata &metadata, int number_of_groups, int number_of_parts);
+
+namespace metadataio {
+    bool operator==(const LpMetadataBlockDevice &a, const LpMetadataBlockDevice &b);
+    bool operator==(const LpMetadataPartitionGroup &a, const LpMetadataPartitionGroup &b);
+    bool operator==(const LpMetadataExtent &a, const LpMetadataExtent &b);
+    bool operator==(const LpMetadataPartition &a, const LpMetadataPartition &b);
+    bool operator==(const LpMetadataTableDescriptor &a, const LpMetadataTableDescriptor &b);
+    bool operator==(const LpMetadataGeometry &a, const LpMetadataGeometry &b);
+    bool operator==(const LpMetadataHeader &a, const LpMetadataHeader &b);
+    bool operator==(const android::fs_mgr::LpMetadata &a, const android::fs_mgr::LpMetadata &b);
+
+    std::ostream& operator<<(std::ostream &s, const LpMetadataBlockDevice &m);
+    std::ostream& operator<<(std::ostream &s, const LpMetadataPartitionGroup &m);
+    std::ostream& operator<<(std::ostream &s, const LpMetadataExtent &m);
+    std::ostream& operator<<(std::ostream &s, const LpMetadataPartition &m);
+    std::ostream& operator<<(std::ostream &s, const LpMetadataTableDescriptor &m);
+    std::ostream& operator<<(std::ostream &s, const LpMetadataGeometry &m);
+    std::ostream& operator<<(std::ostream &s, const LpMetadataHeader &m);
+    std::ostream& operator<<(std::ostream &s, const android::fs_mgr::LpMetadata &m);
+
+    std::istream& operator>>(std::istream &s, LpMetadataBlockDevice &m);
+    std::istream& operator>>(std::istream &s, LpMetadataPartitionGroup &m);
+    std::istream& operator>>(std::istream &s, LpMetadataExtent &m);
+    std::istream& operator>>(std::istream &s, LpMetadataPartition &m);
+    std::istream& operator>>(std::istream &s, LpMetadataTableDescriptor &m);
+    std::istream& operator>>(std::istream &s, LpMetadataGeometry &m);
+    std::istream& operator>>(std::istream &s, LpMetadataHeader &m);
+    std::istream& operator>>(std::istream &s, android::fs_mgr::LpMetadata &m);
+
+    void readMetadata(android::fs_mgr::LpMetadata &m, const std::string &s);
+}
+
+#endif // _METADATAIO_H_
\ No newline at end of file
diff --git a/src/dynamic-partitions/testlib/super_dump.cpp b/src/dynamic-partitions/testlib/super_dump.cpp
new file mode 100644 (file)
index 0000000..e61f2da
--- /dev/null
@@ -0,0 +1,28 @@
+/* Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * SPDX-License-Identifier: MIT */
+
+#include <iostream>
+#include <liblp/liblp.h>
+
+#include "metadataio.h"
+
+using namespace std;
+using namespace metadataio;
+
+int main(int argc, char **argv) {
+    if(argc != 2) {
+        cout << "Usage: " << argv[0] << " path_to_super" << endl;
+        return 1;
+    }
+
+    auto metadata = android::fs_mgr::ReadMetadata(argv[1], 0);
+
+    if (!metadata) {
+        cerr << "Failed to parse metadata from \"" << argv[1] << "\"" << endl;
+        return 1;
+    }
+
+    cout << *metadata << endl;
+
+    return 0;
+}
\ No newline at end of file
diff --git a/src/parse-dynparts/.clang-format b/src/parse-dynparts/.clang-format
deleted file mode 100644 (file)
index 06e3c51..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
----
-Language: Cpp
-BasedOnStyle: Google
-...
diff --git a/src/parse-dynparts/.gitignore b/src/parse-dynparts/.gitignore
deleted file mode 100644 (file)
index 796b96d..0000000
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/src/parse-dynparts/.vscode/extensions.json b/src/parse-dynparts/.vscode/extensions.json
deleted file mode 100644 (file)
index a6a5990..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-{
-       // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
-       // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
-       // List of extensions which should be recommended for users of this workspace.
-       "recommendations": [
-               "ms-vscode.cpptools",
-               "ms-vscode.cmake-tools",
-               "twxs.cmake",
-       ],
-       // List of extensions recommended by VS Code that should not be recommended for users of this workspace.
-       "unwantedRecommendations": []
-}
diff --git a/src/parse-dynparts/.vscode/settings.json b/src/parse-dynparts/.vscode/settings.json
deleted file mode 100644 (file)
index add6fba..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-{
-       "C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools",
-       "cmake.configureOnOpen": true
-}
diff --git a/src/parse-dynparts/CMakeLists.txt b/src/parse-dynparts/CMakeLists.txt
deleted file mode 100644 (file)
index dfb6e1b..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-set(CMAKE_CXX_STANDARD 17)
-set(CMAKE_CXX_STANDARD_REQUIRED True)
-
-add_subdirectory(liblp)
-
-add_executable(parse-dynparts main.cpp lib.cpp)
-target_compile_options(parse-dynparts PRIVATE -Wall -Wextra -pedantic -fPIE)
-
-target_link_libraries(parse-dynparts PRIVATE lp)
-install(TARGETS parse-dynparts DESTINATION ${INSTALL_DIR})
-
-add_executable(parse-dynparts-test test/test.cpp lib.cpp test/metadataio.h test/metadataio.cpp)
-target_compile_options(parse-dynparts-test PRIVATE -Wall -Wextra -pedantic -fPIE -Wno-stringop-truncation)
-target_link_libraries(parse-dynparts-test PRIVATE GTest::gtest_main lp)
-
-include(GoogleTest)
-gtest_discover_tests(parse-dynparts-test)
diff --git a/src/parse-dynparts/LICENSE b/src/parse-dynparts/LICENSE
deleted file mode 100644 (file)
index 261eeb9..0000000
+++ /dev/null
@@ -1,201 +0,0 @@
-                                 Apache License
-                           Version 2.0, January 2004
-                        http://www.apache.org/licenses/
-
-   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-   1. Definitions.
-
-      "License" shall mean the terms and conditions for use, reproduction,
-      and distribution as defined by Sections 1 through 9 of this document.
-
-      "Licensor" shall mean the copyright owner or entity authorized by
-      the copyright owner that is granting the License.
-
-      "Legal Entity" shall mean the union of the acting entity and all
-      other entities that control, are controlled by, or are under common
-      control with that entity. For the purposes of this definition,
-      "control" means (i) the power, direct or indirect, to cause the
-      direction or management of such entity, whether by contract or
-      otherwise, or (ii) ownership of fifty percent (50%) or more of the
-      outstanding shares, or (iii) beneficial ownership of such entity.
-
-      "You" (or "Your") shall mean an individual or Legal Entity
-      exercising permissions granted by this License.
-
-      "Source" form shall mean the preferred form for making modifications,
-      including but not limited to software source code, documentation
-      source, and configuration files.
-
-      "Object" form shall mean any form resulting from mechanical
-      transformation or translation of a Source form, including but
-      not limited to compiled object code, generated documentation,
-      and conversions to other media types.
-
-      "Work" shall mean the work of authorship, whether in Source or
-      Object form, made available under the License, as indicated by a
-      copyright notice that is included in or attached to the work
-      (an example is provided in the Appendix below).
-
-      "Derivative Works" shall mean any work, whether in Source or Object
-      form, that is based on (or derived from) the Work and for which the
-      editorial revisions, annotations, elaborations, or other modifications
-      represent, as a whole, an original work of authorship. For the purposes
-      of this License, Derivative Works shall not include works that remain
-      separable from, or merely link (or bind by name) to the interfaces of,
-      the Work and Derivative Works thereof.
-
-      "Contribution" shall mean any work of authorship, including
-      the original version of the Work and any modifications or additions
-      to that Work or Derivative Works thereof, that is intentionally
-      submitted to Licensor for inclusion in the Work by the copyright owner
-      or by an individual or Legal Entity authorized to submit on behalf of
-      the copyright owner. For the purposes of this definition, "submitted"
-      means any form of electronic, verbal, or written communication sent
-      to the Licensor or its representatives, including but not limited to
-      communication on electronic mailing lists, source code control systems,
-      and issue tracking systems that are managed by, or on behalf of, the
-      Licensor for the purpose of discussing and improving the Work, but
-      excluding communication that is conspicuously marked or otherwise
-      designated in writing by the copyright owner as "Not a Contribution."
-
-      "Contributor" shall mean Licensor and any individual or Legal Entity
-      on behalf of whom a Contribution has been received by Licensor and
-      subsequently incorporated within the Work.
-
-   2. Grant of Copyright License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      copyright license to reproduce, prepare Derivative Works of,
-      publicly display, publicly perform, sublicense, and distribute the
-      Work and such Derivative Works in Source or Object form.
-
-   3. Grant of Patent License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      (except as stated in this section) patent license to make, have made,
-      use, offer to sell, sell, import, and otherwise transfer the Work,
-      where such license applies only to those patent claims licensable
-      by such Contributor that are necessarily infringed by their
-      Contribution(s) alone or by combination of their Contribution(s)
-      with the Work to which such Contribution(s) was submitted. If You
-      institute patent litigation against any entity (including a
-      cross-claim or counterclaim in a lawsuit) alleging that the Work
-      or a Contribution incorporated within the Work constitutes direct
-      or contributory patent infringement, then any patent licenses
-      granted to You under this License for that Work shall terminate
-      as of the date such litigation is filed.
-
-   4. Redistribution. You may reproduce and distribute copies of the
-      Work or Derivative Works thereof in any medium, with or without
-      modifications, and in Source or Object form, provided that You
-      meet the following conditions:
-
-      (a) You must give any other recipients of the Work or
-          Derivative Works a copy of this License; and
-
-      (b) You must cause any modified files to carry prominent notices
-          stating that You changed the files; and
-
-      (c) You must retain, in the Source form of any Derivative Works
-          that You distribute, all copyright, patent, trademark, and
-          attribution notices from the Source form of the Work,
-          excluding those notices that do not pertain to any part of
-          the Derivative Works; and
-
-      (d) If the Work includes a "NOTICE" text file as part of its
-          distribution, then any Derivative Works that You distribute must
-          include a readable copy of the attribution notices contained
-          within such NOTICE file, excluding those notices that do not
-          pertain to any part of the Derivative Works, in at least one
-          of the following places: within a NOTICE text file distributed
-          as part of the Derivative Works; within the Source form or
-          documentation, if provided along with the Derivative Works; or,
-          within a display generated by the Derivative Works, if and
-          wherever such third-party notices normally appear. The contents
-          of the NOTICE file are for informational purposes only and
-          do not modify the License. You may add Your own attribution
-          notices within Derivative Works that You distribute, alongside
-          or as an addendum to the NOTICE text from the Work, provided
-          that such additional attribution notices cannot be construed
-          as modifying the License.
-
-      You may add Your own copyright statement to Your modifications and
-      may provide additional or different license terms and conditions
-      for use, reproduction, or distribution of Your modifications, or
-      for any such Derivative Works as a whole, provided Your use,
-      reproduction, and distribution of the Work otherwise complies with
-      the conditions stated in this License.
-
-   5. Submission of Contributions. Unless You explicitly state otherwise,
-      any Contribution intentionally submitted for inclusion in the Work
-      by You to the Licensor shall be under the terms and conditions of
-      this License, without any additional terms or conditions.
-      Notwithstanding the above, nothing herein shall supersede or modify
-      the terms of any separate license agreement you may have executed
-      with Licensor regarding such Contributions.
-
-   6. Trademarks. This License does not grant permission to use the trade
-      names, trademarks, service marks, or product names of the Licensor,
-      except as required for reasonable and customary use in describing the
-      origin of the Work and reproducing the content of the NOTICE file.
-
-   7. Disclaimer of Warranty. Unless required by applicable law or
-      agreed to in writing, Licensor provides the Work (and each
-      Contributor provides its Contributions) on an "AS IS" BASIS,
-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-      implied, including, without limitation, any warranties or conditions
-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-      PARTICULAR PURPOSE. You are solely responsible for determining the
-      appropriateness of using or redistributing the Work and assume any
-      risks associated with Your exercise of permissions under this License.
-
-   8. Limitation of Liability. In no event and under no legal theory,
-      whether in tort (including negligence), contract, or otherwise,
-      unless required by applicable law (such as deliberate and grossly
-      negligent acts) or agreed to in writing, shall any Contributor be
-      liable to You for damages, including any direct, indirect, special,
-      incidental, or consequential damages of any character arising as a
-      result of this License or out of the use or inability to use the
-      Work (including but not limited to damages for loss of goodwill,
-      work stoppage, computer failure or malfunction, or any and all
-      other commercial damages or losses), even if such Contributor
-      has been advised of the possibility of such damages.
-
-   9. Accepting Warranty or Additional Liability. While redistributing
-      the Work or Derivative Works thereof, You may choose to offer,
-      and charge a fee for, acceptance of support, warranty, indemnity,
-      or other liability obligations and/or rights consistent with this
-      License. However, in accepting such obligations, You may act only
-      on Your own behalf and on Your sole responsibility, not on behalf
-      of any other Contributor, and only if You agree to indemnify,
-      defend, and hold each Contributor harmless for any liability
-      incurred by, or claims asserted against, such Contributor by reason
-      of your accepting any such warranty or additional liability.
-
-   END OF TERMS AND CONDITIONS
-
-   APPENDIX: How to apply the Apache License to your work.
-
-      To apply the Apache License to your work, attach the following
-      boilerplate notice, with the fields enclosed by brackets "[]"
-      replaced with your own identifying information. (Don't include
-      the brackets!)  The text should be enclosed in the appropriate
-      comment syntax for the file format. We also recommend that a
-      file or class name and description of purpose be included on the
-      same "printed page" as the copyright notice for easier
-      identification within third-party archives.
-
-   Copyright [yyyy] [name of copyright owner]
-
-   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.
diff --git a/src/parse-dynparts/README.md b/src/parse-dynparts/README.md
deleted file mode 100644 (file)
index 848b5c9..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-Purpose
-=======
-Most devices running Android 10 and higher use Android's [Dynamic Partitions][1]
-feature to allow the different read-only system partitions (e.g. `system`,
-`vendor`, `product`) to share the same pool of storage space. This allows
-vendors to safely resize those partitions in OTA updates, as long as the sum of
-their sizes doesn't exceed that of the physical partition they all reside in.
-
-The physical partition image that holds multiple Android dynamic partitions is
-conventionally named `super.img` and holds similar information as an LVM
-physical volume on Linux: a list of logical partitions, each associated with a
-(possibly non-contiguous) set of blocks in the file that comprise it. Like LVM,
-Android makes use of [Device Mapper's dm-linear target][2] to inform the
-kernel of the logical partitions so it can map them to block devices in
-`/dev/mapper`.
-
-In true Google fashion, however, Android dynamic partitions use a totally custom
-header format that is not compatible with LVM or other similar software. As
-such, the only official tools that exist to mount them are part of Android and
-depend heavily on Android's frameworks, volume manager, and init system. (There
-are [official tools][3] that run on Linux to pack and unpack `super.img` files,
-but they cannot mount them in-place.)
-
-This tool makes it possible to mount `super.img` files with a standard Linux
-userspace. It uses a modified version of Google's AOSP code to parse the
-partition layout, then outputs that layout as a textual "concise device
-specification" which, when passed to `dmsetup`, instructs the kernel to create
-a Device Mapper block device for each logical partition in the image.
-
-[1]: https://source.android.com/devices/tech/ota/dynamic_partitions
-[2]: https://www.kernel.org/doc/html/latest/admin-guide/device-mapper/linear.html
-[3]: https://android.googlesource.com/platform/system/extras/+/master/partition_tools/
-
-Dependencies
-============
- - CMake
- - OpenSSL (for hash functions)
-
-Building
-========
-This is a standard C++ CMake project; it builds like any other CMake project.
-For those unfamiliar with CMake, here's the incantation you need to build using
-[Ninja](https://ninja-build.org/) as a backend:
-```
-mkdir build
-cd build
-cmake -G Ninja ..
-ninja
-```
-
-Or, if you don't have Ninja, you can use the Makefile backend:
-```
-mkdir build
-cd build
-cmake ..
-make
-```
-
-Usage
-=====
-
-Setup
------
- 1. Obtain a raw `super.img`. Depending on the source of the image you're
-    working with, this may initially be a sparse image, which you'll have to
-    unsparse using the standard Android `simg2img` tool, or it may be one
-    partition inside a GPT-partitioned disk image.
- 2. Make your `super.img` available as a loop device (omit `-r` if you want to
-    allow writes):
-    ```
-    losetup -r /dev/loop0 super.img
-    ```
- 3. Create mappings for the dynamic partitions:
-    ```
-    dmsetup create --concise "$(parse-dynparts /dev/loop0)"
-    ```
- 4. Access your partitions as `/dev/mapper/dynpart-<NAME>`!
-
-Teardown
---------
- 1. Unmap the Device Mapper devices:
-    ```
-    dmsetup remove /dev/mapper/dynpart-*
-    ```
- 2. Delete the loop device:
-    ```
-    losetup -d /dev/loop0
-    ```
diff --git a/src/parse-dynparts/lib.cpp b/src/parse-dynparts/lib.cpp
deleted file mode 100644 (file)
index 1981b05..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-/* Copyright (c) 2021 Tom Hebb (https://github.com/tchebb/parse-android-dynparts)
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
- * SPDX-License-Identifier: Apache-2.0 */
-
-#include "lib.hpp"
-
-#include <iostream>
-#include <sstream>
-#include <string_view>
-
-using namespace android::fs_mgr;
-using std::endl;
-
-std::optional<std::string> go(const android::fs_mgr::LpMetadata &metadata, bool list_tables, std::string_view device_name, std::ostream &messages) {
-  std::string table;
-
-  // Code structure taken from Android's system/core/fs_mgr/fs_mgr_dm_linear.cpp
-  for (const auto& partition : metadata.partitions) {
-    if (!partition.num_extents) {
-      messages << "Skipping zero-length logical partition: "
-               << GetPartitionName(partition) << endl;
-      continue;
-    }
-    if (partition.attributes & LP_PARTITION_ATTR_DISABLED) {
-      messages << "Skipping disabled partition: " << GetPartitionName(partition)
-               << endl;
-      continue;
-    }
-
-    std::ostringstream line;
-
-    if (list_tables)
-      line << GetPartitionName(partition) << " ";
-    else {
-      bool read_only = partition.attributes & LP_PARTITION_ATTR_READONLY;
-      line << GetPartitionName(partition) << ",,," << (read_only ? "ro," : "rw,");
-    }
-
-    uint64_t sector = 0;
-    for (size_t i = 0; i < partition.num_extents; i++) {
-      const auto& extent = metadata.extents[partition.first_extent_index + i];
-      switch (extent.target_type) {
-        case LP_TARGET_TYPE_ZERO:
-          line << sector << " " << extent.num_sectors << " zero";
-          break;
-        case LP_TARGET_TYPE_LINEAR: {
-          if (extent.target_source != 0) {
-            messages << "This utility does not yet support multiple block devices"
-                     << endl;
-            return std::nullopt;
-          }
-
-          if (i && !list_tables) line << ",";
-          if (i && list_tables) line << "\\n";
-
-          line << sector << " " << extent.num_sectors << " linear "
-               << device_name << " " << extent.target_data;
-          break;
-        }
-        default:
-          messages << "Unknown target type in metadata: " << extent.target_type
-                << endl;
-          return std::nullopt;
-      }
-      sector += extent.num_sectors;
-    }
-
-    if (!table.empty() && list_tables) table += "\n";
-    if (!table.empty() && !list_tables) table += ";";
-    table += line.str();
-  }
-
-  return table;
-}
\ No newline at end of file
diff --git a/src/parse-dynparts/lib.hpp b/src/parse-dynparts/lib.hpp
deleted file mode 100644 (file)
index 86aa29d..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-/* Copyright (c) 2021 Tom Hebb (https://github.com/tchebb/parse-android-dynparts)
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
- * SPDX-License-Identifier: Apache-2.0 */
-
-#pragma once
-
-#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
diff --git a/src/parse-dynparts/liblp/CMakeLists.txt b/src/parse-dynparts/liblp/CMakeLists.txt
deleted file mode 100644 (file)
index 44f3c8a..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-add_library(lp STATIC reader.cpp utility.cpp)
-
-target_include_directories(lp PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
-target_compile_options(lp PRIVATE -Wall -Wextra -pedantic -fPIE -Wno-stringop-truncation)
-
-find_package(OpenSSL REQUIRED)
-target_link_libraries(lp PRIVATE OpenSSL::Crypto)
\ No newline at end of file
diff --git a/src/parse-dynparts/liblp/README.md b/src/parse-dynparts/liblp/README.md
deleted file mode 100644 (file)
index 77d012f..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-liblp
-=====
-
-This code is based on `fs_mgr/liblp` from Android's `platform/system/core`
-project. However, I've removed most of the files and edited the ones that remain
-so that they build independently from the rest of the AOSP codebase. All
-functions still declared in the local copy of `include/liblp/liblp.h` ought to
-work. `CMakeLists.txt` is not from upstream and was authored by me to replace
-upstream's `Android.bp`.
-
-Currently derived from the upstream tree at [commit f9c36a2ca632][1].
-
-[1]: https://android.googlesource.com/platform/system/core/+/f9c36a2ca632fa88edba9c2c87f14f2aec1e7fd3/fs_mgr/liblp/
diff --git a/src/parse-dynparts/liblp/include/liblp/liblp.h b/src/parse-dynparts/liblp/include/liblp/liblp.h
deleted file mode 100644 (file)
index 14f6ccf..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-//
-// 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.
-//
-
-#ifndef LIBLP_LIBLP_H
-#define LIBLP_LIBLP_H
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <map>
-#include <memory>
-#include <string>
-
-#include "metadata_format.h"
-
-namespace android {
-namespace fs_mgr {
-
-// Helper structure for easily interpreting deserialized metadata, or
-// re-serializing metadata.
-struct LpMetadata {
-    LpMetadataGeometry geometry;
-    LpMetadataHeader header;
-    std::vector<LpMetadataPartition> partitions;
-    std::vector<LpMetadataExtent> extents;
-    std::vector<LpMetadataPartitionGroup> groups;
-    std::vector<LpMetadataBlockDevice> block_devices;
-};
-//
-// Read logical partition metadata from its predetermined location on a block
-// device. If readback fails, we also attempt to load from a backup copy.
-std::unique_ptr<LpMetadata> ReadMetadata(const std::string& super_partition, uint32_t slot_number);
-
-// Helper to extract safe C++ strings from partition info.
-std::string GetPartitionName(const LpMetadataPartition& partition);
-std::string GetPartitionGroupName(const LpMetadataPartitionGroup& group);
-std::string GetBlockDevicePartitionName(const LpMetadataBlockDevice& block_device);
-
-// Return the block device that houses the super partition metadata; returns
-// null on failure.
-const LpMetadataBlockDevice* GetMetadataSuperBlockDevice(const LpMetadata& metadata);
-
-// Return the total size of all partitions comprising the super partition.
-uint64_t GetTotalSuperPartitionSize(const LpMetadata& metadata);
-
-// Get the list of block device names required by the given metadata.
-std::vector<std::string> GetBlockDevicePartitionNames(const LpMetadata& metadata);
-
-// Slot suffix helpers.
-uint32_t SlotNumberForSlotSuffix(const std::string& suffix);
-std::string SlotSuffixForSlotNumber(uint32_t slot_number);
-std::string GetPartitionSlotSuffix(const std::string& partition_name);
-
-// Helpers for common functions.
-const LpMetadataPartition* FindPartition(const LpMetadata& metadata, const std::string& name);
-uint64_t GetPartitionSize(const LpMetadata& metadata, const LpMetadataPartition& partition);
-
-}  // namespace fs_mgr
-}  // namespace android
-
-#endif  // LIBLP_LIBLP_H
diff --git a/src/parse-dynparts/liblp/include/liblp/metadata_format.h b/src/parse-dynparts/liblp/include/liblp/metadata_format.h
deleted file mode 100644 (file)
index 41d8b0c..0000000
+++ /dev/null
@@ -1,403 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef LOGICAL_PARTITION_METADATA_FORMAT_H_
-#define LOGICAL_PARTITION_METADATA_FORMAT_H_
-
-#ifdef __cplusplus
-#include <string>
-#include <vector>
-#endif
-
-#include <stdint.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* Magic signature for LpMetadataGeometry. */
-#define LP_METADATA_GEOMETRY_MAGIC 0x616c4467
-
-/* Space reserved for geometry information. */
-#define LP_METADATA_GEOMETRY_SIZE 4096
-
-/* Magic signature for LpMetadataHeader. */
-#define LP_METADATA_HEADER_MAGIC 0x414C5030
-
-/* Current metadata version. */
-#define LP_METADATA_MAJOR_VERSION 10
-#define LP_METADATA_MINOR_VERSION_MIN 0
-#define LP_METADATA_MINOR_VERSION_MAX 2
-
-/* Metadata version needed to use the UPDATED partition attribute. */
-#define LP_METADATA_VERSION_FOR_UPDATED_ATTR 1
-
-/* Metadata version needed for the new expanded header struct. */
-#define LP_METADATA_VERSION_FOR_EXPANDED_HEADER 2
-
-/* Attributes for the LpMetadataPartition::attributes field.
- *
- * READONLY - The partition should not be considered writable. When used with
- * device mapper, the block device will be created as read-only.
- */
-#define LP_PARTITION_ATTR_NONE 0x0
-#define LP_PARTITION_ATTR_READONLY (1 << 0)
-
-/* This flag is only intended to be used with super_empty.img and super.img on
- * retrofit devices. On these devices there are A and B super partitions, and
- * we don't know ahead of time which slot the image will be applied to.
- *
- * If set, the partition name needs a slot suffix applied. The slot suffix is
- * determined by the metadata slot number (0 = _a, 1 = _b).
- */
-#define LP_PARTITION_ATTR_SLOT_SUFFIXED (1 << 1)
-
-/* This flag is applied automatically when using MetadataBuilder::NewForUpdate.
- * It signals that the partition was created (or modified) for a snapshot-based
- * update. If this flag is not present, the partition was likely flashed via
- * fastboot.
- */
-#define LP_PARTITION_ATTR_UPDATED (1 << 2)
-
-/* This flag marks a partition as disabled. It should not be used or mapped. */
-#define LP_PARTITION_ATTR_DISABLED (1 << 3)
-
-/* Mask that defines all valid attributes. When changing this, make sure to
- * update ParseMetadata().
- */
-#define LP_PARTITION_ATTRIBUTE_MASK_V0 \
-    (LP_PARTITION_ATTR_READONLY | LP_PARTITION_ATTR_SLOT_SUFFIXED)
-#define LP_PARTITION_ATTRIBUTE_MASK_V1 (LP_PARTITION_ATTR_UPDATED | LP_PARTITION_ATTR_DISABLED)
-#define LP_PARTITION_ATTRIBUTE_MASK \
-    (LP_PARTITION_ATTRIBUTE_MASK_V0 | LP_PARTITION_ATTRIBUTE_MASK_V1)
-
-/* Default name of the physical partition that holds logical partition entries.
- * The layout of this partition will look like:
- *
- *     +--------------------+
- *     | Disk Geometry      |
- *     +--------------------+
- *     | Geometry Backup    |
- *     +--------------------+
- *     | Metadata           |
- *     +--------------------+
- *     | Backup Metadata    |
- *     +--------------------+
- *     | Logical Partitions |
- *     +--------------------+
- */
-#define LP_METADATA_DEFAULT_PARTITION_NAME "super"
-
-/* Size of a sector is always 512 bytes for compatibility with the Linux kernel. */
-#define LP_SECTOR_SIZE 512
-
-/* Amount of space reserved at the start of every super partition to avoid
- * creating an accidental boot sector.
- */
-#define LP_PARTITION_RESERVED_BYTES 4096
-
-/* This structure is stored at block 0 in the first 4096 bytes of the
- * partition, and again in the following block. It is never modified and
- * describes how logical partition information can be located.
- */
-typedef struct LpMetadataGeometry {
-    /*  0: Magic signature (LP_METADATA_GEOMETRY_MAGIC). */
-    uint32_t magic;
-
-    /*  4: Size of the LpMetadataGeometry struct. */
-    uint32_t struct_size;
-
-    /*  8: SHA256 checksum of this struct, with this field set to 0. */
-    uint8_t checksum[32];
-
-    /* 40: Maximum amount of space a single copy of the metadata can use. This
-     * must be a multiple of LP_SECTOR_SIZE.
-     */
-    uint32_t metadata_max_size;
-
-    /* 44: Number of copies of the metadata to keep. For A/B devices, this
-     * will be 2. For an A/B/C device, it would be 3, et cetera. For Non-A/B
-     * it will be 1. A backup copy of each slot is kept, so if this is "2",
-     * there will be four copies total.
-     */
-    uint32_t metadata_slot_count;
-
-    /* 48: Logical block size. This is the minimal alignment for partition and
-     * extent sizes, and it must be a multiple of LP_SECTOR_SIZE. Note that
-     * this must be equal across all LUNs that comprise the super partition,
-     * and thus this field is stored in the geometry, not per-device.
-     */
-    uint32_t logical_block_size;
-} __attribute__((packed)) LpMetadataGeometry;
-
-/* The logical partition metadata has a number of tables; they are described
- * in the header via the following structure.
- *
- * The size of the table can be computed by multiplying entry_size by
- * num_entries, and the result must not overflow a 32-bit signed integer.
- */
-typedef struct LpMetadataTableDescriptor {
-    /*  0: Location of the table, relative to end of the metadata header. */
-    uint32_t offset;
-    /*  4: Number of entries in the table. */
-    uint32_t num_entries;
-    /*  8: Size of each entry in the table, in bytes. */
-    uint32_t entry_size;
-} __attribute__((packed)) LpMetadataTableDescriptor;
-
-/* Binary format for the header of the logical partition metadata format.
- *
- * The format has three sections. The header must occur first, and the
- * proceeding tables may be placed in any order after.
- *
- *  +-----------------------------------------+
- *  | Header data - fixed size                |
- *  +-----------------------------------------+
- *  | Partition table - variable size         |
- *  +-----------------------------------------+
- *  | Partition table extents - variable size |
- *  +-----------------------------------------+
- *
- * The "Header" portion is described by LpMetadataHeader. It will always
- * precede the other three blocks.
- *
- * All fields are stored in little-endian byte order when serialized.
- *
- * This struct is versioned; see the |major_version| and |minor_version|
- * fields.
- */
-typedef struct LpMetadataHeader {
-    /*  0: Four bytes equal to LP_METADATA_HEADER_MAGIC. */
-    uint32_t magic;
-
-    /*  4: Version number required to read this metadata. If the version is not
-     * equal to the library version, the metadata should be considered
-     * incompatible.
-     */
-    uint16_t major_version;
-
-    /*  6: Minor version. A library supporting newer features should be able to
-     * read metadata with an older minor version. However, an older library
-     * should not support reading metadata if its minor version is higher.
-     */
-    uint16_t minor_version;
-
-    /*  8: The size of this header struct. */
-    uint32_t header_size;
-
-    /* 12: SHA256 checksum of the header, up to |header_size| bytes, computed as
-     * if this field were set to 0.
-     */
-    uint8_t header_checksum[32];
-
-    /* 44: The total size of all tables. This size is contiguous; tables may not
-     * have gaps in between, and they immediately follow the header.
-     */
-    uint32_t tables_size;
-
-    /* 48: SHA256 checksum of all table contents. */
-    uint8_t tables_checksum[32];
-
-    /* 80: Partition table descriptor. */
-    LpMetadataTableDescriptor partitions;
-    /* 92: Extent table descriptor. */
-    LpMetadataTableDescriptor extents;
-    /* 104: Updateable group descriptor. */
-    LpMetadataTableDescriptor groups;
-    /* 116: Block device table. */
-    LpMetadataTableDescriptor block_devices;
-
-    /* Everything past here is header version 1.2+, and is only included if
-     * needed. When liblp supporting >= 1.2 reads a < 1.2 header, it must
-     * zero these additional fields.
-     */
-
-    /* 128: See LP_HEADER_FLAG_ constants for possible values. Header flags are
-     * independent of the version number and intended to be informational only.
-     * New flags can be added without bumping the version.
-     */
-    uint32_t flags;
-
-    /* 132: Reserved (zero), pad to 256 bytes. */
-    uint8_t reserved[124];
-} __attribute__((packed)) LpMetadataHeader;
-
-/* This device uses Virtual A/B. Note that on retrofit devices, the expanded
- * header may not be present.
- */
-#define LP_HEADER_FLAG_VIRTUAL_AB_DEVICE 0x1
-
-/* This struct defines a logical partition entry, similar to what would be
- * present in a GUID Partition Table.
- */
-typedef struct LpMetadataPartition {
-    /*  0: Name of this partition in ASCII characters. Any unused characters in
-     * the buffer must be set to 0. Characters may only be alphanumeric or _.
-     * The name must include at least one ASCII character, and it must be unique
-     * across all partition names. The length (36) is the same as the maximum
-     * length of a GPT partition name.
-     */
-    char name[36];
-
-    /* 36: Attributes for the partition (see LP_PARTITION_ATTR_* flags above). */
-    uint32_t attributes;
-
-    /* 40: Index of the first extent owned by this partition. The extent will
-     * start at logical sector 0. Gaps between extents are not allowed.
-     */
-    uint32_t first_extent_index;
-
-    /* 44: Number of extents in the partition. Every partition must have at
-     * least one extent.
-     */
-    uint32_t num_extents;
-
-    /* 48: Group this partition belongs to. */
-    uint32_t group_index;
-} __attribute__((packed)) LpMetadataPartition;
-
-/* This extent is a dm-linear target, and the index is an index into the
- * LinearExtent table.
- */
-#define LP_TARGET_TYPE_LINEAR 0
-
-/* This extent is a dm-zero target. The index is ignored and must be 0. */
-#define LP_TARGET_TYPE_ZERO 1
-
-/* This struct defines an extent entry in the extent table block. */
-typedef struct LpMetadataExtent {
-    /*  0: Length of this extent, in 512-byte sectors. */
-    uint64_t num_sectors;
-
-    /*  8: Target type for device-mapper (see LP_TARGET_TYPE_* values). */
-    uint32_t target_type;
-
-    /* 12: Contents depends on target_type.
-     *
-     * LINEAR: The sector on the physical partition that this extent maps onto.
-     * ZERO: This field must be 0.
-     */
-    uint64_t target_data;
-
-    /* 20: Contents depends on target_type.
-     *
-     * LINEAR: Must be an index into the block devices table.
-     * ZERO: This field must be 0.
-     */
-    uint32_t target_source;
-} __attribute__((packed)) LpMetadataExtent;
-
-/* This struct defines an entry in the groups table. Each group has a maximum
- * size, and partitions in a group must not exceed that size. There is always
- * a "default" group of unlimited size, which is used when not using update
- * groups or when using overlayfs or fastbootd.
- */
-typedef struct LpMetadataPartitionGroup {
-    /*  0: Name of this group. Any unused characters must be 0. */
-    char name[36];
-
-    /* 36: Flags (see LP_GROUP_*). */
-    uint32_t flags;
-
-    /* 40: Maximum size in bytes. If 0, the group has no maximum size. */
-    uint64_t maximum_size;
-} __attribute__((packed)) LpMetadataPartitionGroup;
-
-/* This flag is only intended to be used with super_empty.img and super.img on
- * retrofit devices. If set, the group needs a slot suffix to be interpreted
- * correctly. The suffix is automatically applied by ReadMetadata().
- */
-#define LP_GROUP_SLOT_SUFFIXED (1 << 0)
-
-/* This struct defines an entry in the block_devices table. There must be at
- * least one device, and the first device must represent the partition holding
- * the super metadata.
- */
-typedef struct LpMetadataBlockDevice {
-    /* 0: First usable sector for allocating logical partitions. this will be
-     * the first sector after the initial geometry blocks, followed by the
-     * space consumed by metadata_max_size*metadata_slot_count*2.
-     */
-    uint64_t first_logical_sector;
-
-    /* 8: Alignment for defining partitions or partition extents. For example,
-     * an alignment of 1MiB will require that all partitions have a size evenly
-     * divisible by 1MiB, and that the smallest unit the partition can grow by
-     * is 1MiB.
-     *
-     * Alignment is normally determined at runtime when growing or adding
-     * partitions. If for some reason the alignment cannot be determined, then
-     * this predefined alignment in the geometry is used instead. By default
-     * it is set to 1MiB.
-     */
-    uint32_t alignment;
-
-    /* 12: Alignment offset for "stacked" devices. For example, if the "super"
-     * partition itself is not aligned within the parent block device's
-     * partition table, then we adjust for this in deciding where to place
-     * |first_logical_sector|.
-     *
-     * Similar to |alignment|, this will be derived from the operating system.
-     * If it cannot be determined, it is assumed to be 0.
-     */
-    uint32_t alignment_offset;
-
-    /* 16: Block device size, as specified when the metadata was created. This
-     * can be used to verify the geometry against a target device.
-     */
-    uint64_t size;
-
-    /* 24: Partition name in the GPT. Any unused characters must be 0. */
-    char partition_name[36];
-
-    /* 60: Flags (see LP_BLOCK_DEVICE_* flags below). */
-    uint32_t flags;
-} __attribute__((packed)) LpMetadataBlockDevice;
-
-/* This flag is only intended to be used with super_empty.img and super.img on
- * retrofit devices. On these devices there are A and B super partitions, and
- * we don't know ahead of time which slot the image will be applied to.
- *
- * If set, the block device needs a slot suffix applied before being used with
- * IPartitionOpener. The slot suffix is determined by the metadata slot number
- * (0 = _a, 1 = _b).
- */
-#define LP_BLOCK_DEVICE_SLOT_SUFFIXED (1 << 0)
-
-/* For ease of writing compatibility checks, the original metadata header is
- * preserved below, and typedefs are provided for the current version.
- */
-typedef struct LpMetadataHeaderV1_0 {
-    uint32_t magic;
-    uint16_t major_version;
-    uint16_t minor_version;
-    uint32_t header_size;
-    uint8_t header_checksum[32];
-    uint32_t tables_size;
-    uint8_t tables_checksum[32];
-    LpMetadataTableDescriptor partitions;
-    LpMetadataTableDescriptor extents;
-    LpMetadataTableDescriptor groups;
-    LpMetadataTableDescriptor block_devices;
-} __attribute__((packed)) LpMetadataHeaderV1_0;
-
-typedef LpMetadataHeader LpMetadataHeaderV1_2;
-
-#ifdef __cplusplus
-} /* extern "C" */
-#endif
-
-#endif /* LOGICAL_PARTITION_METADATA_FORMAT_H_ */
diff --git a/src/parse-dynparts/liblp/reader.cpp b/src/parse-dynparts/liblp/reader.cpp
deleted file mode 100644 (file)
index 4f291be..0000000
+++ /dev/null
@@ -1,502 +0,0 @@
-/*
- * 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.
- */
-
-#include "reader.h"
-
-#include <fcntl.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <functional>
-
-#include "utility.h"
-
-namespace android {
-namespace fs_mgr {
-
-static_assert(sizeof(LpMetadataHeaderV1_0) == offsetof(LpMetadataHeader, flags),
-              "Incorrect LpMetadataHeader v0 size");
-
-// Helper class for reading descriptors and memory buffers in the same manner.
-class Reader {
-  public:
-    virtual ~Reader(){};
-    virtual bool ReadFully(void* buffer, size_t length) = 0;
-};
-
-class FileReader final : public Reader {
-  public:
-    explicit FileReader(int fd) : fd_(fd) {}
-    bool ReadFully(void* buffer, size_t length) override {
-        return android::fs_mgr::ReadFully(fd_, buffer, length);
-    }
-
-  private:
-    int fd_;
-};
-
-class MemoryReader final : public Reader {
-  public:
-    MemoryReader(const void* buffer, size_t size)
-        : buffer_(reinterpret_cast<const uint8_t*>(buffer)), size_(size), pos_(0) {}
-    bool ReadFully(void* out, size_t length) override {
-        if (size_ - pos_ < length) {
-            errno = EINVAL;
-            return false;
-        }
-        memcpy(out, buffer_ + pos_, length);
-        pos_ += length;
-        return true;
-    }
-
-  private:
-    const uint8_t* buffer_;
-    size_t size_;
-    size_t pos_;
-};
-
-bool ParseGeometry(const void* buffer, LpMetadataGeometry* geometry) {
-    static_assert(sizeof(*geometry) <= LP_METADATA_GEOMETRY_SIZE);
-    memcpy(geometry, buffer, sizeof(*geometry));
-
-    // Check the magic signature.
-    if (geometry->magic != LP_METADATA_GEOMETRY_MAGIC) {
-        LERROR << "Logical partition metadata has invalid geometry magic signature.";
-        return false;
-    }
-    // Reject if the struct size is larger than what we compiled. This is so we
-    // can compute a checksum with the |struct_size| field rather than using
-    // sizeof.
-    if (geometry->struct_size > sizeof(LpMetadataGeometry)) {
-        LERROR << "Logical partition metadata has unrecognized fields.";
-        return false;
-    }
-    // Recompute and check the CRC32.
-    {
-        LpMetadataGeometry temp = *geometry;
-        memset(&temp.checksum, 0, sizeof(temp.checksum));
-        SHA256(&temp, temp.struct_size, temp.checksum);
-        if (memcmp(temp.checksum, geometry->checksum, sizeof(temp.checksum)) != 0) {
-            LERROR << "Logical partition metadata has invalid geometry checksum.";
-            return false;
-        }
-    }
-    // Check that the struct size is equal (this will have to change if we ever
-    // change the struct size in a release).
-    if (geometry->struct_size != sizeof(LpMetadataGeometry)) {
-        LERROR << "Logical partition metadata has invalid struct size.";
-        return false;
-    }
-    if (geometry->metadata_slot_count == 0) {
-        LERROR << "Logical partition metadata has invalid slot count.";
-        return false;
-    }
-    if (geometry->metadata_max_size % LP_SECTOR_SIZE != 0) {
-        LERROR << "Metadata max size is not sector-aligned.";
-        return false;
-    }
-    return true;
-}
-
-bool ReadPrimaryGeometry(int fd, LpMetadataGeometry* geometry) {
-    std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(LP_METADATA_GEOMETRY_SIZE);
-    if (SeekFile64(fd, GetPrimaryGeometryOffset(), SEEK_SET) < 0) {
-        PERROR << __PRETTY_FUNCTION__ << " lseek failed";
-        return false;
-    }
-    if (!ReadFully(fd, buffer.get(), LP_METADATA_GEOMETRY_SIZE)) {
-        PERROR << __PRETTY_FUNCTION__ << " read " << LP_METADATA_GEOMETRY_SIZE << " bytes failed";
-        return false;
-    }
-    return ParseGeometry(buffer.get(), geometry);
-}
-
-bool ReadBackupGeometry(int fd, LpMetadataGeometry* geometry) {
-    std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(LP_METADATA_GEOMETRY_SIZE);
-    if (SeekFile64(fd, GetBackupGeometryOffset(), SEEK_SET) < 0) {
-        PERROR << __PRETTY_FUNCTION__ << " lseek failed";
-        return false;
-    }
-    if (!ReadFully(fd, buffer.get(), LP_METADATA_GEOMETRY_SIZE)) {
-        PERROR << __PRETTY_FUNCTION__ << " backup read " << LP_METADATA_GEOMETRY_SIZE
-               << " bytes failed";
-        return false;
-    }
-    return ParseGeometry(buffer.get(), geometry);
-}
-
-// Read and validate geometry information from a block device that holds
-// logical partitions. If the information is corrupted, this will attempt
-// to read it from a secondary backup location.
-bool ReadLogicalPartitionGeometry(int fd, LpMetadataGeometry* geometry) {
-    if (ReadPrimaryGeometry(fd, geometry)) {
-        return true;
-    }
-    return ReadBackupGeometry(fd, geometry);
-}
-
-static bool ValidateTableBounds(const LpMetadataHeader& header,
-                                const LpMetadataTableDescriptor& table) {
-    if (table.offset > header.tables_size) {
-        return false;
-    }
-    uint64_t table_size = uint64_t(table.num_entries) * table.entry_size;
-    if (header.tables_size - table.offset < table_size) {
-        return false;
-    }
-    return true;
-}
-
-static bool ReadMetadataHeader(Reader* reader, LpMetadata* metadata) {
-    // Note we zero the struct since older files will result in a partial read.
-    LpMetadataHeader& header = metadata->header;
-    memset(&header, 0, sizeof(header));
-
-    if (!reader->ReadFully(&header, sizeof(LpMetadataHeaderV1_0))) {
-        PERROR << __PRETTY_FUNCTION__ << " read failed";
-        return false;
-    }
-
-    // Do basic validity checks before computing the checksum.
-    if (header.magic != LP_METADATA_HEADER_MAGIC) {
-        LERROR << "Logical partition metadata has invalid magic value.";
-        return false;
-    }
-    if (header.major_version != LP_METADATA_MAJOR_VERSION ||
-        header.minor_version > LP_METADATA_MINOR_VERSION_MAX) {
-        LERROR << "Logical partition metadata has incompatible version.";
-        return false;
-    }
-
-    // Validate the header struct size against the reported version.
-    uint32_t expected_struct_size = sizeof(header);
-    if (header.minor_version < LP_METADATA_VERSION_FOR_EXPANDED_HEADER) {
-        expected_struct_size = sizeof(LpMetadataHeaderV1_0);
-    }
-    if (header.header_size != expected_struct_size) {
-        LERROR << "Invalid partition metadata header struct size.";
-        return false;
-    }
-
-    // Read in any remaining fields, the last step needed before checksumming.
-    if (size_t remaining_bytes = header.header_size - sizeof(LpMetadataHeaderV1_0)) {
-        uint8_t* offset = reinterpret_cast<uint8_t*>(&header) + sizeof(LpMetadataHeaderV1_0);
-        if (!reader->ReadFully(offset, remaining_bytes)) {
-            PERROR << __PRETTY_FUNCTION__ << " read failed";
-            return false;
-        }
-    }
-
-    // To compute the header's checksum, we have to temporarily set its checksum
-    // field to 0. Note that we must only compute up to |header_size|.
-    {
-        LpMetadataHeader temp = header;
-        memset(&temp.header_checksum, 0, sizeof(temp.header_checksum));
-        SHA256(&temp, temp.header_size, temp.header_checksum);
-        if (memcmp(temp.header_checksum, header.header_checksum, sizeof(temp.header_checksum)) !=
-            0) {
-            LERROR << "Logical partition metadata has invalid checksum.";
-            return false;
-        }
-    }
-
-    if (!ValidateTableBounds(header, header.partitions) ||
-        !ValidateTableBounds(header, header.extents) ||
-        !ValidateTableBounds(header, header.groups) ||
-        !ValidateTableBounds(header, header.block_devices)) {
-        LERROR << "Logical partition metadata has invalid table bounds.";
-        return false;
-    }
-    // Check that table entry sizes can accomodate their respective structs. If
-    // table sizes change, these checks will have to be adjusted.
-    if (header.partitions.entry_size != sizeof(LpMetadataPartition)) {
-        LERROR << "Logical partition metadata has invalid partition table entry size.";
-        return false;
-    }
-    if (header.extents.entry_size != sizeof(LpMetadataExtent)) {
-        LERROR << "Logical partition metadata has invalid extent table entry size.";
-        return false;
-    }
-    if (header.groups.entry_size != sizeof(LpMetadataPartitionGroup)) {
-        LERROR << "Logical partition metadata has invalid group table entry size.";
-        return false;
-    }
-    return true;
-}
-
-// Parse and validate all metadata at the current position in the given file
-// descriptor.
-static std::unique_ptr<LpMetadata> ParseMetadata(const LpMetadataGeometry& geometry,
-                                                 Reader* reader) {
-    // First read and validate the header.
-    std::unique_ptr<LpMetadata> metadata = std::make_unique<LpMetadata>();
-
-    metadata->geometry = geometry;
-    if (!ReadMetadataHeader(reader, metadata.get())) {
-        return nullptr;
-    }
-
-    LpMetadataHeader& header = metadata->header;
-
-    // Check the table size.
-    if (header.tables_size > geometry.metadata_max_size) {
-        LERROR << "Invalid partition metadata header table size.";
-        return nullptr;
-    }
-
-    // Read the metadata payload. Allocation is fallible since the table size
-    // could be large.
-    std::unique_ptr<uint8_t[]> buffer(new (std::nothrow) uint8_t[header.tables_size]);
-    if (!buffer) {
-        LERROR << "Out of memory reading logical partition tables.";
-        return nullptr;
-    }
-    if (!reader->ReadFully(buffer.get(), header.tables_size)) {
-        PERROR << __PRETTY_FUNCTION__ << " read " << header.tables_size << "bytes failed";
-        return nullptr;
-    }
-
-    uint8_t checksum[32];
-    SHA256(buffer.get(), header.tables_size, checksum);
-    if (memcmp(checksum, header.tables_checksum, sizeof(checksum)) != 0) {
-        LERROR << "Logical partition metadata has invalid table checksum.";
-        return nullptr;
-    }
-
-    uint32_t valid_attributes = LP_PARTITION_ATTRIBUTE_MASK_V0;
-    if (metadata->header.minor_version >= LP_METADATA_VERSION_FOR_UPDATED_ATTR) {
-        valid_attributes |= LP_PARTITION_ATTRIBUTE_MASK_V1;
-    }
-
-    // ValidateTableSize ensured that |cursor| is valid for the number of
-    // entries in the table.
-    uint8_t* cursor = buffer.get() + header.partitions.offset;
-    for (size_t i = 0; i < header.partitions.num_entries; i++) {
-        LpMetadataPartition partition;
-        memcpy(&partition, cursor, sizeof(partition));
-        cursor += header.partitions.entry_size;
-
-        if (partition.attributes & ~valid_attributes) {
-            LERROR << "Logical partition has invalid attribute set.";
-            return nullptr;
-        }
-        if (partition.first_extent_index + partition.num_extents < partition.first_extent_index) {
-            LERROR << "Logical partition first_extent_index + num_extents overflowed.";
-            return nullptr;
-        }
-        if (partition.first_extent_index + partition.num_extents > header.extents.num_entries) {
-            LERROR << "Logical partition has invalid extent list.";
-            return nullptr;
-        }
-        if (partition.group_index >= header.groups.num_entries) {
-            LERROR << "Logical partition has invalid group index.";
-            return nullptr;
-        }
-
-        metadata->partitions.push_back(partition);
-    }
-
-    cursor = buffer.get() + header.extents.offset;
-    for (size_t i = 0; i < header.extents.num_entries; i++) {
-        LpMetadataExtent extent;
-        memcpy(&extent, cursor, sizeof(extent));
-        cursor += header.extents.entry_size;
-
-        if (extent.target_type == LP_TARGET_TYPE_LINEAR &&
-            extent.target_source >= header.block_devices.num_entries) {
-            LERROR << "Logical partition extent has invalid block device.";
-            return nullptr;
-        }
-
-        metadata->extents.push_back(extent);
-    }
-
-    cursor = buffer.get() + header.groups.offset;
-    for (size_t i = 0; i < header.groups.num_entries; i++) {
-        LpMetadataPartitionGroup group = {};
-        memcpy(&group, cursor, sizeof(group));
-        cursor += header.groups.entry_size;
-
-        metadata->groups.push_back(group);
-    }
-
-    cursor = buffer.get() + header.block_devices.offset;
-    for (size_t i = 0; i < header.block_devices.num_entries; i++) {
-        LpMetadataBlockDevice device = {};
-        memcpy(&device, cursor, sizeof(device));
-        cursor += header.block_devices.entry_size;
-
-        metadata->block_devices.push_back(device);
-    }
-
-    const LpMetadataBlockDevice* super_device = GetMetadataSuperBlockDevice(*metadata.get());
-    if (!super_device) {
-        LERROR << "Metadata does not specify a super device.";
-        return nullptr;
-    }
-
-    // Check that the metadata area and logical partition areas don't overlap.
-    uint64_t metadata_region =
-            GetTotalMetadataSize(geometry.metadata_max_size, geometry.metadata_slot_count);
-    if (metadata_region > super_device->first_logical_sector * LP_SECTOR_SIZE) {
-        LERROR << "Logical partition metadata overlaps with logical partition contents.";
-        return nullptr;
-    }
-    return metadata;
-}
-
-std::unique_ptr<LpMetadata> ParseMetadata(const LpMetadataGeometry& geometry, const void* buffer,
-                                          size_t size) {
-    MemoryReader reader(buffer, size);
-    return ParseMetadata(geometry, &reader);
-}
-
-std::unique_ptr<LpMetadata> ParseMetadata(const LpMetadataGeometry& geometry, int fd) {
-    FileReader reader(fd);
-    return ParseMetadata(geometry, &reader);
-}
-
-std::unique_ptr<LpMetadata> ReadPrimaryMetadata(int fd, const LpMetadataGeometry& geometry,
-                                                uint32_t slot_number) {
-    int64_t offset = GetPrimaryMetadataOffset(geometry, slot_number);
-    if (SeekFile64(fd, offset, SEEK_SET) < 0) {
-        PERROR << __PRETTY_FUNCTION__ << " lseek failed: offset " << offset;
-        return nullptr;
-    }
-    return ParseMetadata(geometry, fd);
-}
-
-std::unique_ptr<LpMetadata> ReadBackupMetadata(int fd, const LpMetadataGeometry& geometry,
-                                               uint32_t slot_number) {
-    int64_t offset = GetBackupMetadataOffset(geometry, slot_number);
-    if (SeekFile64(fd, offset, SEEK_SET) < 0) {
-        PERROR << __PRETTY_FUNCTION__ << " lseek failed: offset " << offset;
-        return nullptr;
-    }
-    return ParseMetadata(geometry, fd);
-}
-
-namespace {
-
-bool AdjustMetadataForSlot(LpMetadata* metadata, uint32_t slot_number) {
-    std::string slot_suffix = SlotSuffixForSlotNumber(slot_number);
-    for (auto& partition : metadata->partitions) {
-        if (!(partition.attributes & LP_PARTITION_ATTR_SLOT_SUFFIXED)) {
-            continue;
-        }
-        std::string partition_name = GetPartitionName(partition) + slot_suffix;
-        if (partition_name.size() > sizeof(partition.name)) {
-            LERROR << __PRETTY_FUNCTION__ << " partition name too long: " << partition_name;
-            return false;
-        }
-        strncpy(partition.name, partition_name.c_str(), sizeof(partition.name));
-        partition.attributes &= ~LP_PARTITION_ATTR_SLOT_SUFFIXED;
-    }
-    for (auto& block_device : metadata->block_devices) {
-        if (!(block_device.flags & LP_BLOCK_DEVICE_SLOT_SUFFIXED)) {
-            continue;
-        }
-        std::string partition_name = GetBlockDevicePartitionName(block_device) + slot_suffix;
-        if (!UpdateBlockDevicePartitionName(&block_device, partition_name)) {
-            LERROR << __PRETTY_FUNCTION__ << " partition name too long: " << partition_name;
-            return false;
-        }
-        block_device.flags &= ~LP_BLOCK_DEVICE_SLOT_SUFFIXED;
-    }
-    for (auto& group : metadata->groups) {
-        if (!(group.flags & LP_GROUP_SLOT_SUFFIXED)) {
-            continue;
-        }
-        std::string group_name = GetPartitionGroupName(group) + slot_suffix;
-        if (!UpdatePartitionGroupName(&group, group_name)) {
-            LERROR << __PRETTY_FUNCTION__ << " group name too long: " << group_name;
-            return false;
-        }
-        group.flags &= ~LP_GROUP_SLOT_SUFFIXED;
-    }
-    return true;
-}
-
-}  // namespace
-
-std::unique_ptr<LpMetadata> ReadMetadata(const std::string& super_partition, uint32_t slot_number) {
-    int fd = open(super_partition.c_str(), O_RDONLY);
-    if (fd < 0) {
-        PERROR << __PRETTY_FUNCTION__ << " open failed: " << super_partition;
-        return nullptr;
-    }
-
-    LpMetadataGeometry geometry;
-    if (!ReadLogicalPartitionGeometry(fd, &geometry)) {
-        close(fd);
-        return nullptr;
-    }
-    if (slot_number >= geometry.metadata_slot_count) {
-        LERROR << __PRETTY_FUNCTION__ << " invalid metadata slot number";
-        close(fd);
-        return nullptr;
-    }
-
-    std::vector<int64_t> offsets = {
-            GetPrimaryMetadataOffset(geometry, slot_number),
-            GetBackupMetadataOffset(geometry, slot_number),
-    };
-    std::unique_ptr<LpMetadata> metadata;
-
-    for (const auto& offset : offsets) {
-        if (SeekFile64(fd, offset, SEEK_SET) < 0) {
-            PERROR << __PRETTY_FUNCTION__ << " lseek failed, offset " << offset;
-            continue;
-        }
-        if ((metadata = ParseMetadata(geometry, fd)) != nullptr) {
-            break;
-        }
-    }
-    if (!metadata || !AdjustMetadataForSlot(metadata.get(), slot_number)) {
-        close(fd);
-        return nullptr;
-    }
-
-    close(fd);
-    return metadata;
-}
-
-static std::string NameFromFixedArray(const char* name, size_t buffer_size) {
-    // If the end of the buffer has a null character, it's safe to assume the
-    // buffer is null terminated. Otherwise, we cap the string to the input
-    // buffer size.
-    if (name[buffer_size - 1] == '\0') {
-        return std::string(name);
-    }
-    return std::string(name, buffer_size);
-}
-
-std::string GetPartitionName(const LpMetadataPartition& partition) {
-    return NameFromFixedArray(partition.name, sizeof(partition.name));
-}
-
-std::string GetPartitionGroupName(const LpMetadataPartitionGroup& group) {
-    return NameFromFixedArray(group.name, sizeof(group.name));
-}
-
-std::string GetBlockDevicePartitionName(const LpMetadataBlockDevice& block_device) {
-    return NameFromFixedArray(block_device.partition_name, sizeof(block_device.partition_name));
-}
-
-}  // namespace fs_mgr
-}  // namespace android
diff --git a/src/parse-dynparts/liblp/reader.h b/src/parse-dynparts/liblp/reader.h
deleted file mode 100644 (file)
index 7a2490b..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef LIBLP_READER_H_
-#define LIBLP_READER_H_
-
-#include <stddef.h>
-
-#include <memory>
-
-#include <liblp/liblp.h>
-
-namespace android {
-namespace fs_mgr {
-
-// Parse an LpMetadataGeometry from a buffer. The buffer must be at least
-// LP_METADATA_GEOMETRY_SIZE bytes in size.
-bool ParseGeometry(const void* buffer, LpMetadataGeometry* geometry);
-
-// Helper functions for manually reading geometry and metadata.
-std::unique_ptr<LpMetadata> ParseMetadata(const LpMetadataGeometry& geometry, int fd);
-std::unique_ptr<LpMetadata> ParseMetadata(const LpMetadataGeometry& geometry, const void* buffer,
-                                          size_t size);
-bool ReadLogicalPartitionGeometry(int fd, LpMetadataGeometry* geometry);
-bool ReadPrimaryGeometry(int fd, LpMetadataGeometry* geometry);
-bool ReadBackupGeometry(int fd, LpMetadataGeometry* geometry);
-
-// These functions assume a valid geometry and slot number, and do not obey
-// auto-slot-suffixing. They are used for tests and for checking whether
-// the metadata is coherent across primary and backup copies.
-std::unique_ptr<LpMetadata> ReadPrimaryMetadata(int fd, const LpMetadataGeometry& geometry,
-                                                uint32_t slot_number);
-std::unique_ptr<LpMetadata> ReadBackupMetadata(int fd, const LpMetadataGeometry& geometry,
-                                               uint32_t slot_number);
-
-}  // namespace fs_mgr
-}  // namespace android
-
-#endif /* LIBLP_READER_H_ */
diff --git a/src/parse-dynparts/liblp/utility.cpp b/src/parse-dynparts/liblp/utility.cpp
deleted file mode 100644 (file)
index afab237..0000000
+++ /dev/null
@@ -1,233 +0,0 @@
-/*
- * 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.
- */
-
-#include <fcntl.h>
-#include <inttypes.h>
-#include <stdint.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-#if defined(__linux__)
-#include <linux/fs.h>
-#include <sys/ioctl.h>
-#endif
-
-#include <cstring>
-#include <map>
-#include <string>
-#include <vector>
-
-#include <openssl/sha.h>
-#include <openssl/evp.h>
-
-#include "utility.h"
-
-namespace android {
-namespace fs_mgr {
-
-int64_t SeekFile64(int fd, int64_t offset, int whence) {
-    static_assert(sizeof(off_t) == sizeof(int64_t), "Need 64-bit lseek");
-    return lseek(fd, offset, whence);
-}
-
-int64_t GetPrimaryGeometryOffset() {
-    return LP_PARTITION_RESERVED_BYTES;
-}
-
-int64_t GetBackupGeometryOffset() {
-    return GetPrimaryGeometryOffset() + LP_METADATA_GEOMETRY_SIZE;
-}
-
-int64_t GetPrimaryMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number) {
-    CHECK(slot_number < geometry.metadata_slot_count);
-    int64_t offset = LP_PARTITION_RESERVED_BYTES + (LP_METADATA_GEOMETRY_SIZE * 2) +
-                     geometry.metadata_max_size * slot_number;
-    return offset;
-}
-
-int64_t GetBackupMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number) {
-    CHECK(slot_number < geometry.metadata_slot_count);
-    int64_t start = LP_PARTITION_RESERVED_BYTES + (LP_METADATA_GEOMETRY_SIZE * 2) +
-                    int64_t(geometry.metadata_max_size) * geometry.metadata_slot_count;
-    return start + int64_t(geometry.metadata_max_size * slot_number);
-}
-
-uint64_t GetTotalMetadataSize(uint32_t metadata_max_size, uint32_t max_slots) {
-    return LP_PARTITION_RESERVED_BYTES +
-           (LP_METADATA_GEOMETRY_SIZE + metadata_max_size * max_slots) * 2;
-}
-
-const LpMetadataBlockDevice* GetMetadataSuperBlockDevice(const LpMetadata& metadata) {
-    if (metadata.block_devices.empty()) {
-        return nullptr;
-    }
-    return &metadata.block_devices[0];
-}
-
-void SHA256(const void* data, size_t length, uint8_t out[32]) {
-    const EVP_MD *md = EVP_sha256();
-
-    if (!EVP_Digest(data, length, out, nullptr, md, nullptr))
-        LERROR << __PRETTY_FUNCTION__ << "Unable to compute hash";
-}
-
-uint32_t SlotNumberForSlotSuffix(const std::string& suffix) {
-    if (suffix.empty() || suffix == "a" || suffix == "_a") {
-        return 0;
-    } else if (suffix == "b" || suffix == "_b") {
-        return 1;
-    } else {
-        LERROR << __PRETTY_FUNCTION__ << "slot '" << suffix
-               << "' does not have a recognized format.";
-        return 0;
-    }
-}
-
-uint64_t GetTotalSuperPartitionSize(const LpMetadata& metadata) {
-    uint64_t size = 0;
-    for (const auto& block_device : metadata.block_devices) {
-        size += block_device.size;
-    }
-    return size;
-}
-
-std::vector<std::string> GetBlockDevicePartitionNames(const LpMetadata& metadata) {
-    std::vector<std::string> list;
-    for (const auto& block_device : metadata.block_devices) {
-        list.emplace_back(GetBlockDevicePartitionName(block_device));
-    }
-    return list;
-}
-
-const LpMetadataPartition* FindPartition(const LpMetadata& metadata, const std::string& name) {
-    for (const auto& partition : metadata.partitions) {
-        if (GetPartitionName(partition) == name) {
-            return &partition;
-        }
-    }
-    return nullptr;
-}
-
-uint64_t GetPartitionSize(const LpMetadata& metadata, const LpMetadataPartition& partition) {
-    uint64_t total_size = 0;
-    for (uint32_t i = 0; i < partition.num_extents; i++) {
-        const auto& extent = metadata.extents[partition.first_extent_index + i];
-        total_size += extent.num_sectors * LP_SECTOR_SIZE;
-    }
-    return total_size;
-}
-
-std::string GetPartitionSlotSuffix(const std::string& partition_name) {
-    if (partition_name.size() <= 2) {
-        return "";
-    }
-    std::string suffix = partition_name.substr(partition_name.size() - 2);
-    return (suffix == "_a" || suffix == "_b") ? suffix : "";
-}
-
-std::string SlotSuffixForSlotNumber(uint32_t slot_number) {
-    CHECK(slot_number == 0 || slot_number == 1);
-    return (slot_number == 0) ? "_a" : "_b";
-}
-
-bool UpdateBlockDevicePartitionName(LpMetadataBlockDevice* device, const std::string& name) {
-    if (name.size() > sizeof(device->partition_name)) {
-        return false;
-    }
-    strncpy(device->partition_name, name.c_str(), sizeof(device->partition_name));
-    return true;
-}
-
-bool UpdatePartitionGroupName(LpMetadataPartitionGroup* group, const std::string& name) {
-    if (name.size() > sizeof(group->name)) {
-        return false;
-    }
-    strncpy(group->name, name.c_str(), sizeof(group->name));
-    return true;
-}
-
-bool UpdatePartitionName(LpMetadataPartition* partition, const std::string& name) {
-    if (name.size() > sizeof(partition->name)) {
-        return false;
-    }
-    strncpy(partition->name, name.c_str(), sizeof(partition->name));
-    return true;
-}
-
-bool SetBlockReadonly(int fd, bool readonly) {
-#if defined(__linux__)
-    int val = readonly;
-    return ioctl(fd, BLKROSET, &val) == 0;
-#else
-    (void)fd;
-    (void)readonly;
-    return true;
-#endif
-}
-
-inline std::string ToHexString(uint64_t value) {
-    std::ostringstream stream;
-    stream << "0x" << std::hex << value;
-    return stream.str();
-}
-
-void SetMetadataHeaderV0(LpMetadata* metadata) {
-    if (metadata->header.minor_version <= LP_METADATA_MINOR_VERSION_MIN) {
-        return;
-    }
-    LINFO << "Forcefully setting metadata header version " << LP_METADATA_MAJOR_VERSION << "."
-          << metadata->header.minor_version << " to " << LP_METADATA_MAJOR_VERSION << "."
-          << LP_METADATA_MINOR_VERSION_MIN;
-    metadata->header.minor_version = LP_METADATA_MINOR_VERSION_MIN;
-    metadata->header.header_size = sizeof(LpMetadataHeaderV1_0);
-
-    // Retrofit Virtual A/B devices should have version 10.1, so flags shouldn't be set.
-    // Warn if this is the case, but zero it out anyways.
-    if (metadata->header.flags) {
-        LWARN << "Zeroing unexpected flags: " << ToHexString(metadata->header.flags);
-    }
-
-    // Zero out all fields beyond LpMetadataHeaderV0.
-    static_assert(sizeof(metadata->header) > sizeof(LpMetadataHeaderV1_0));
-    memset(reinterpret_cast<uint8_t*>(&metadata->header) + sizeof(LpMetadataHeaderV1_0), 0,
-           sizeof(metadata->header) - sizeof(LpMetadataHeaderV1_0));
-
-    // Clear partition attributes unknown to V0.
-    // On retrofit Virtual A/B devices, UPDATED flag may be set, so only log info here.
-    for (auto& partition : metadata->partitions) {
-        if (partition.attributes & ~LP_PARTITION_ATTRIBUTE_MASK_V0) {
-            LINFO << "Clearing " << GetPartitionName(partition)
-                  << " partition attribute: " << ToHexString(partition.attributes);
-        }
-
-        partition.attributes &= LP_PARTITION_ATTRIBUTE_MASK_V0;
-    }
-}
-
-bool ReadFully(int fd, void* data, size_t byte_count) {
-  uint8_t* p = reinterpret_cast<uint8_t*>(data);
-  size_t remaining = byte_count;
-  while (remaining > 0) {
-    ssize_t n = TEMP_FAILURE_RETRY(read(fd, p, remaining));
-    if (n <= 0) return false;
-    p += n;
-    remaining -= n;
-  }
-  return true;
-}
-
-}  // namespace fs_mgr
-}  // namespace android
diff --git a/src/parse-dynparts/liblp/utility.h b/src/parse-dynparts/liblp/utility.h
deleted file mode 100644 (file)
index 6b17abe..0000000
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef LIBLP_UTILITY_H
-#define LIBLP_UTILITY_H
-
-#include <stddef.h>
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <cassert>
-#include <iostream>
-#include <limits>
-#include <sstream>
-#include <string>
-#include <string_view>
-
-#include <liblp/liblp.h>
-
-#define LP_TAG "[liblp]"
-#define LWARN NewlineLogger(std::cerr).stream << "[W]" << LP_TAG
-#define LINFO NewlineLogger(std::cerr).stream << "[I]" << LP_TAG
-#define LERROR NewlineLogger(std::cerr).stream << "[E]" << LP_TAG
-#define PWARNING LWARN
-#define PERROR LERROR
-
-#define CHECK assert
-
-namespace android {
-namespace fs_mgr {
-
-// Return the offset of the primary or backup geometry.
-int64_t GetPrimaryGeometryOffset();
-int64_t GetBackupGeometryOffset();
-
-// Return the offset of a primary metadata slot, relative to the start of the
-// device.
-int64_t GetPrimaryMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number);
-
-// Return the offset of a backup metadata slot, relative to the end of the
-// device.
-int64_t GetBackupMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number);
-
-// Return the total space at the start of the super partition that must be set
-// aside from headers/metadata and backups.
-uint64_t GetTotalMetadataSize(uint32_t metadata_max_size, uint32_t max_slots);
-
-// Cross-platform helper for lseek64().
-int64_t SeekFile64(int fd, int64_t offset, int whence);
-
-// Compute a SHA256 hash.
-void SHA256(const void* data, size_t length, uint8_t out[32]);
-
-// Align |base| such that it is evenly divisible by |alignment|, which does not
-// have to be a power of two. Return false on overflow.
-template <typename T>
-bool AlignTo(T base, uint32_t alignment, T* out) {
-    static_assert(std::numeric_limits<T>::is_integer);
-    static_assert(!std::numeric_limits<T>::is_signed);
-    if (!alignment) {
-        *out = base;
-        return true;
-    }
-    T remainder = base % alignment;
-    if (remainder == 0) {
-        *out = base;
-        return true;
-    }
-    T to_add = alignment - remainder;
-    if (to_add > std::numeric_limits<T>::max() - base) {
-        return false;
-    }
-    *out = base + to_add;
-    return true;
-}
-
-// Update names from C++ strings.
-bool UpdateBlockDevicePartitionName(LpMetadataBlockDevice* device, const std::string& name);
-bool UpdatePartitionGroupName(LpMetadataPartitionGroup* group, const std::string& name);
-bool UpdatePartitionName(LpMetadataPartition* partition, const std::string& name);
-
-// Call BLKROSET ioctl on fd so that fd is readonly / read-writable.
-bool SetBlockReadonly(int fd, bool readonly);
-
-// Forcefully set metadata header version to 1.0, clearing any incompatible flags and attributes
-// so that when downgrading to a build with liblp V0, the device still boots.
-void SetMetadataHeaderV0(LpMetadata* metadata);
-
-bool ReadFully(int fd, void* data, size_t byte_count);
-
-class NewlineLogger {
-  public:
-    NewlineLogger(std::ostream& sink) : sink_(sink) {}
-    ~NewlineLogger() {
-        sink_ << stream.str() << std::endl;
-    }
-
-    std::ostringstream stream;
-
-  private:
-    std::ostream& sink_;
-};
-
-}  // namespace fs_mgr
-}  // namespace android
-
-#endif  // LIBLP_UTILITY_H
diff --git a/src/parse-dynparts/main.cpp b/src/parse-dynparts/main.cpp
deleted file mode 100644 (file)
index 35fe9b5..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-/* Copyright (c) 2021 Tom Hebb (https://github.com/tchebb/parse-android-dynparts)
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
- * SPDX-License-Identifier: Apache-2.0 */
-
-#include <liblp/liblp.h>
-
-#include <iostream>
-#include <sstream>
-#include <string_view>
-
-#include "lib.hpp"
-
-using namespace android::fs_mgr;
-
-using std::cerr;
-using std::cout;
-using std::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;
-    return 1;
-  }
-
-  bool list_tables = (argc == 3) ? true : false;
-
-  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);
-  if (out)
-    cout << *out << "\n";
-  else
-    return 1;
-
-  return 0;
-}
diff --git a/src/parse-dynparts/test/generate_test_data.sh b/src/parse-dynparts/test/generate_test_data.sh
deleted file mode 100755 (executable)
index 0c99cb9..0000000
+++ /dev/null
@@ -1,339 +0,0 @@
- #!/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
-
-    super_size_mb=6 # MB
-    super_size="$(($super_size_mb*$MIB))"
-    group_size="$(($super_size-$metadata_aligned_size))"
-    part_size="$group_size" # 5MB
-
-    lpmake -F --device-size=$super_size \
-           --metadata-size=$metadata_size \
-           --metadata-slots=$metadata_slots \
-           -o=super_1.img \
-           -g "group_a:$group_size" \
-           -p "part_1_a:none:$part_size:group_a"
-}
-
-# super with one slot, two partitions
-create_super_2 () {
-    metadata_slots=1
-    metadata_size=65536
-    metadata_aligned_size="$(get_aligned_size $metadata_size)" #should be 1 MB
-
-    super_size_mb=11 # MB
-    super_size="$(($super_size_mb*$MIB))"
-    group_size="$(($super_size-$metadata_aligned_size))"
-    part_size="$(($group_size/2))" # 5MB
-
-    lpmake -F --device-size=$super_size \
-           --metadata-size=$metadata_size \
-           --metadata-slots=$metadata_slots \
-           -o=super_2.img \
-           -g "group_a:$group_size" \
-           -p "part_1_a:none:$part_size:group_a" \
-           -p "part_2_a:none:$part_size:group_a"
-}
-
-# super with one slot, three partitions
-# super has 31 MB becuase without metadata there is 60 MB for partitions which nicely divides by 3 and by 6
-create_super_3 () {
-    metadata_slots=1
-    metadata_size=65536
-    metadata_aligned_size="$(get_aligned_size $metadata_size)" #should be 1 MB
-
-    super_size_mb=16 # MB
-    super_size="$(($super_size_mb*$MIB))"
-    group_size="$(($super_size-$metadata_aligned_size))"
-    part_size="$(($group_size/3))" # 5MB
-
-    lpmake -F --device-size=$super_size \
-           --metadata-size=$metadata_size \
-           --metadata-slots=$metadata_slots \
-           -o=super_3.img \
-           -g "group_a:$group_size" \
-           -p "part_1_a:none:$part_size:group_a" \
-           -p "part_2_a:none:$part_size:group_a" \
-           -p "part_3_a:none:$part_size:group_a"
-}
-
-# super with one slot, four partitions
-create_super_4 () {
-    metadata_slots=1
-    metadata_size=65536
-    metadata_aligned_size="$(get_aligned_size $metadata_size)" #should be 1 MB
-
-    super_size_mb=21 # MB
-    super_size="$(($super_size_mb*$MIB))"
-    group_size="$(($super_size-$metadata_aligned_size))"
-    part_size="$(($group_size/4))" # 5MB
-
-    lpmake -F --device-size=$super_size \
-           --metadata-size=$metadata_size \
-           --metadata-slots=$metadata_slots \
-           -o=super_4.img \
-           -g "group_a:$group_size" \
-           -p "part_1_a:none:$part_size:group_a" \
-           -p "part_2_a:none:$part_size:group_a" \
-           -p "part_3_a:none:$part_size:group_a" \
-           -p "part_4_a:none:$part_size:group_a"
-}
-
-# super with two slots, one partition each
-create_super_5 () {
-    metadata_slots=2
-    metadata_size=65536
-    metadata_aligned_size="$(get_aligned_size $metadata_size)" #should be 1 MB
-
-    super_size_mb=11 # MB
-    super_size="$(($super_size_mb*$MIB))"
-    group_size="$((($super_size-$metadata_aligned_size)/2))"
-    part_size="$group_size" # 5MB
-
-    lpmake -F --device-size=$super_size \
-           --metadata-size=$metadata_size \
-           --metadata-slots=$metadata_slots \
-           -o=super_5.img \
-           -g "group_a:$group_size" \
-           -p "part_1_a:none:$part_size:group_a" \
-           -g "group_b:$group_size" \
-           -p "part_1_b:none:$part_size:group_b"
-}
-
-# super with two slots, two partitions each
-create_super_6 () {
-    metadata_slots=2
-    metadata_size=65536
-    metadata_aligned_size="$(get_aligned_size $metadata_size)" #should be 1 MB
-
-    super_size_mb=21 # MB
-    super_size="$(($super_size_mb*$MIB))"
-    group_size="$((($super_size-$metadata_aligned_size)/2))"
-    part_size="$(($group_size/2))" # 5MB
-
-    lpmake -F --device-size=$super_size \
-           --metadata-size=$metadata_size \
-           --metadata-slots=$metadata_slots \
-           -o=super_6.img \
-           -g "group_a:$group_size" \
-           -p "part_1_a:none:$part_size:group_a" \
-           -p "part_2_a:none:$part_size:group_a" \
-           -g "group_b:$group_size" \
-           -p "part_1_b:none:$part_size:group_b" \
-           -p "part_2_b:none:$part_size:group_b"
-}
-
-# super with two slots, three partitions each
-create_super_7 () {
-    metadata_slots=2
-    metadata_size=65536
-    metadata_aligned_size="$(get_aligned_size $metadata_size)" #should be 1 MB
-
-    super_size_mb=31 # MB
-    super_size="$(($super_size_mb*$MIB))"
-    group_size="$((($super_size-$metadata_aligned_size)/2))"
-    part_size="$(($group_size/3))" # 5MB
-
-    lpmake -F --device-size=$super_size \
-           --metadata-size=$metadata_size \
-           --metadata-slots=$metadata_slots \
-           -o=super_7.img \
-           -g "group_a:$group_size" \
-           -p "part_1_a:none:$part_size:group_a" \
-           -p "part_2_a:none:$part_size:group_a" \
-           -p "part_3_a:none:$part_size:group_a" \
-           -g "group_b:$group_size" \
-           -p "part_1_b:none:$part_size:group_b" \
-           -p "part_2_b:none:$part_size:group_b" \
-           -p "part_3_b:none:$part_size:group_b"
-}
-
-# super with two slots, four partitions each
-create_super_8 () {
-    metadata_slots=2
-    metadata_size=65536
-    metadata_aligned_size="$(get_aligned_size $metadata_size)" #should be 1 MB
-
-    super_size_mb=41 # MB
-    super_size="$(($super_size_mb*$MIB))"
-    group_size="$((($super_size-$metadata_aligned_size)/2))"
-    part_size="$(($group_size/4))" # 10 MB
-
-    lpmake -F --device-size=$super_size \
-           --metadata-size=$metadata_size \
-           --metadata-slots=$metadata_slots \
-           -o=super_8.img \
-           -g "group_a:$group_size" \
-           -p "part_1_a:none:$part_size:group_a" \
-           -p "part_2_a:none:$part_size:group_a" \
-           -p "part_3_a:none:$part_size:group_a" \
-           -p "part_4_a:none:$part_size:group_a" \
-           -g "group_b:$group_size" \
-           -p "part_1_b:none:$part_size:group_b" \
-           -p "part_2_b:none:$part_size:group_b" \
-           -p "part_3_b:none:$part_size:group_b" \
-           -p "part_4_b:none:$part_size:group_b"
-}
-
-create_super_1
-create_super_2
-create_super_3
-create_super_4
-create_super_5
-create_super_6
-create_super_7
-create_super_8
-
-one_mib_part_size_blocks="$(($MIB/512))"
-two_mib_part_size_blocks="$((2*$MIB/512))"
-three_mib_part_size_blocks="$((3*$MIB/512))"
-four_mib_part_size_blocks="$((4*$MIB/512))"
-five_mib_part_size_blocks="$((5*$MIB/512))"
-ten_mib_part_size_blocks="$((10*$MIB/512))"
-
-dd if=/dev/null of=part_one_mib.img seek="$one_mib_part_size_blocks"
-dd if=/dev/null of=part_two_mib.img seek="$two_mib_part_size_blocks"
-dd if=/dev/null of=part_three_mib.img seek="$three_mib_part_size_blocks"
-dd if=/dev/null of=part_four_mib.img seek="$four_mib_part_size_blocks"
-dd if=/dev/null of=part_five_mib.img seek="$five_mib_part_size_blocks"
-dd if=/dev/null of=part_ten_mib.img seek="$ten_mib_part_size_blocks"
-
-cp super_3.img super_partitioned_1.img
-
-lpadd super_partitioned_1.img part_1_a group_a part_one_mib.img --replace
-lpadd super_partitioned_1.img part_3_a group_a part_one_mib.img --replace
-lpadd super_partitioned_1.img part_4_a group_a part_five_mib.img
-
-cp super_3.img super_partitioned_2.img
-
-lpadd super_partitioned_2.img part_1_a group_a part_one_mib.img --replace
-lpadd super_partitioned_2.img part_3_a group_a part_one_mib.img --replace
-lpadd super_partitioned_2.img part_4_a group_a part_five_mib.img
-lpadd super_partitioned_2.img part_2_a group_a part_one_mib.img --replace
-lpadd super_partitioned_2.img part_5_a group_a part_five_mib.img
-
-cp super_4.img super_partitioned_3.img
-
-lpadd super_partitioned_3.img part_3_a group_a part_one_mib.img --replace
-lpadd super_partitioned_3.img part_2_a group_a part_one_mib.img --replace
-lpadd super_partitioned_3.img part_1_a group_a part_one_mib.img --replace
-lpadd super_partitioned_3.img part_5_a group_a part_ten_mib.img
-
-cp super_7.img super_partitioned_4.img
-
-lpadd super_partitioned_4.img part_1_a group_a part_one_mib.img --replace
-lpadd super_partitioned_4.img part_3_a group_a part_one_mib.img --replace
-lpadd super_partitioned_4.img part_4_a group_a part_five_mib.img
-
-cp super_7.img super_partitioned_5.img
-
-lpadd super_partitioned_5.img part_1_b group_b part_one_mib.img --replace
-lpadd super_partitioned_5.img part_3_b group_b part_one_mib.img --replace
-lpadd super_partitioned_5.img part_4_b group_b part_five_mib.img
-
-cp super_7.img super_partitioned_6.img
-
-lpadd super_partitioned_6.img part_1_a group_a part_one_mib.img --replace
-lpadd super_partitioned_6.img part_3_a group_a part_one_mib.img --replace
-lpadd super_partitioned_6.img part_4_a group_a part_five_mib.img
-
-lpadd super_partitioned_6.img part_1_b group_b part_one_mib.img --replace
-lpadd super_partitioned_6.img part_3_b group_b part_one_mib.img --replace
-lpadd super_partitioned_6.img part_4_b group_b part_five_mib.img
-
-cp super_8.img super_partitioned_7.img
-
-lpadd super_partitioned_7.img part_3_a group_a part_one_mib.img --replace
-lpadd super_partitioned_7.img part_2_a group_a part_one_mib.img --replace
-lpadd super_partitioned_7.img part_1_a group_a part_one_mib.img --replace
-lpadd super_partitioned_7.img part_5_a group_a part_ten_mib.img
-
-cp super_8.img super_partitioned_8.img
-
-lpadd super_partitioned_8.img part_3_b group_b part_one_mib.img --replace
-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
-lpadd super_partitioned_9.img part_2_a group_a part_one_mib.img --replace
-lpadd super_partitioned_9.img part_1_a group_a part_one_mib.img --replace
-lpadd super_partitioned_9.img part_5_a group_a part_ten_mib.img
-
-lpadd super_partitioned_9.img part_3_b group_b part_one_mib.img --replace
-lpadd super_partitioned_9.img part_2_b group_b part_one_mib.img --replace
-lpadd super_partitioned_9.img part_1_b group_b part_one_mib.img --replace
-lpadd super_partitioned_9.img part_5_b group_b part_ten_mib.img
-
-# super_dump is a simple tool which reads metadata
-# from an image and prints it to stdin using metadataio.h
-super_dump super_1.img > super_1.in
-super_dump super_2.img > super_2.in
-super_dump super_3.img > super_3.in
-super_dump super_4.img > super_4.in
-super_dump super_5.img > super_5.in
-super_dump super_6.img > super_6.in
-super_dump super_7.img > super_7.in
-super_dump super_8.img > super_8.in
-super_dump super_partitioned_1.img > super_partitioned_1.in
-super_dump super_partitioned_2.img > super_partitioned_2.in
-super_dump super_partitioned_3.img > super_partitioned_3.in
-super_dump super_partitioned_4.img > super_partitioned_4.in
-super_dump super_partitioned_5.img > super_partitioned_5.in
-super_dump super_partitioned_6.img > super_partitioned_6.in
-super_dump super_partitioned_7.img > super_partitioned_7.in
-super_dump super_partitioned_8.img > super_partitioned_8.in
-super_dump super_partitioned_9.img > super_partitioned_9.in
-
-parse-dynparts super_1.img > super_1.out
-parse-dynparts super_2.img > super_2.out
-parse-dynparts super_3.img > super_3.out
-parse-dynparts super_4.img > super_4.out
-parse-dynparts super_5.img > super_5.out
-parse-dynparts super_6.img > super_6.out
-parse-dynparts super_7.img > super_7.out
-parse-dynparts super_8.img > super_8.out
-parse-dynparts super_partitioned_1.img > super_partitioned_1.out
-parse-dynparts super_partitioned_2.img > super_partitioned_2.out
-parse-dynparts super_partitioned_3.img > super_partitioned_3.out
-parse-dynparts super_partitioned_4.img > super_partitioned_4.out
-parse-dynparts super_partitioned_5.img > super_partitioned_5.out
-parse-dynparts super_partitioned_6.img > super_partitioned_6.out
-parse-dynparts super_partitioned_7.img > super_partitioned_7.out
-parse-dynparts super_partitioned_8.img > super_partitioned_8.out
-parse-dynparts super_partitioned_9.img > super_partitioned_9.out
-
-parse-dynparts super_1.img --list-tables > super_1_list.out
-parse-dynparts super_2.img --list-tables > super_2_list.out
-parse-dynparts super_3.img --list-tables > super_3_list.out
-parse-dynparts super_4.img --list-tables > super_4_list.out
-parse-dynparts super_5.img --list-tables > super_5_list.out
-parse-dynparts super_6.img --list-tables > super_6_list.out
-parse-dynparts super_7.img --list-tables > super_7_list.out
-parse-dynparts super_8.img --list-tables > super_8_list.out
-parse-dynparts super_partitioned_1.img --list-tables > super_partitioned_1_list.out
-parse-dynparts super_partitioned_2.img --list-tables > super_partitioned_2_list.out
-parse-dynparts super_partitioned_3.img --list-tables > super_partitioned_3_list.out
-parse-dynparts super_partitioned_4.img --list-tables > super_partitioned_4_list.out
-parse-dynparts super_partitioned_5.img --list-tables > super_partitioned_5_list.out
-parse-dynparts super_partitioned_6.img --list-tables > super_partitioned_6_list.out
-parse-dynparts super_partitioned_7.img --list-tables > super_partitioned_7_list.out
-parse-dynparts super_partitioned_8.img --list-tables > super_partitioned_8_list.out
-parse-dynparts super_partitioned_9.img --list-tables > super_partitioned_9_list.out
-
diff --git a/src/parse-dynparts/test/metadataio.cpp b/src/parse-dynparts/test/metadataio.cpp
deleted file mode 100644 (file)
index c6f500c..0000000
+++ /dev/null
@@ -1,398 +0,0 @@
-/* Copyright (c) 2023 Samsung Electronics Co., Ltd.
- * SPDX-License-Identifier: MIT */
-
-#include "metadataio.h"
-
-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;
-    if (a.alignment_offset != b.alignment_offset) return false;
-    if (a.size != b.size) return false;
-    if (a.flags != b.flags) return false;
-
-    for(int i = 0; i < 36; ++i)
-        if (a.partition_name[i] != b.partition_name[i]) return false;
-
-    return true;
-}
-
-bool metadataio::operator==(const LpMetadataPartitionGroup &a, const LpMetadataPartitionGroup &b) {
-    if (a.flags != b.flags) return false;
-    if (a.maximum_size != b.maximum_size) return false;
-
-    for(int i = 0; i < 36; ++i)
-        if (a.name[i] != b.name[i]) return false;
-
-    return true;
-}
-
-bool metadataio::operator==(const LpMetadataExtent &a, const LpMetadataExtent &b) {
-    if (a.num_sectors != b.num_sectors) return false;
-    if (a.target_type != b.target_type) return false;
-    if (a.target_data != b.target_data) return false;
-    if (a.target_source != b.target_source) return false;
-    return true;
-}
-
-bool metadataio::operator==(const LpMetadataPartition &a, const LpMetadataPartition &b){
-    if (a.attributes != b.attributes) return false;
-    if (a.first_extent_index != b.first_extent_index) return false;
-    if (a.num_extents != b.num_extents) return false;
-    if (a.group_index != b.group_index) return false;
-
-    for(int i = 0; i < 36; ++i)
-        if (a.name[i] != b.name[i]) return false;
-
-    return true;
-}
-
-bool metadataio::operator==(const LpMetadataTableDescriptor &a, const LpMetadataTableDescriptor &b) {
-    if (a.offset != b.offset) return false;
-    if (a.num_entries != b.num_entries) return false;
-    if (a.entry_size != b.entry_size) return false;
-    return true;
-}
-
-bool metadataio::operator==(const LpMetadataGeometry &a, const LpMetadataGeometry &b) {
-    if (a.magic != b.magic) return false;
-    if (a.struct_size != b.struct_size) return false;
-    if (a.metadata_max_size != b.metadata_max_size) return false;
-    if (a.metadata_slot_count != b.metadata_slot_count) return false;
-    if (a.logical_block_size != b.logical_block_size) return false;
-
-    for(int i = 0; i < 32; ++i)
-        if (a.checksum[i] != b.checksum[i]) return false;
-
-    return true;
-}
-
-bool metadataio::operator==(const LpMetadataHeader &a, const LpMetadataHeader &b) {
-    if (a.magic != b.magic) return false;
-    if (a.major_version != b.major_version) return false;
-    if (a.minor_version != b.minor_version) return false;
-    if (a.header_size != b.header_size) return false;
-    if (a.tables_size != b.tables_size) return false;
-    if (!(a.partitions == b.partitions)) return false;
-    if (!(a.extents == b.extents)) return false;
-    if (!(a.groups == b.groups)) return false;
-    if (!(a.block_devices == b.block_devices)) return false;
-    if (a.flags != b.flags) return false;
-
-    for(int i = 0; i < 32; ++i)
-        if (a.header_checksum[i] != b.header_checksum[i]) return false;
-
-    for(int i = 0; i < 32; ++i)
-        if (a.tables_checksum[i] != b.tables_checksum[i]) return false;
-
-    for(int i = 0; i < 124; ++i)
-        if (a.reserved[i] != b.reserved[i]) return false;
-
-    return true;
-}
-
-bool metadataio::operator==(const android::fs_mgr::LpMetadata &a, const android::fs_mgr::LpMetadata &b) {
-    if (!(a.geometry == b.geometry)) return false;
-    if (!(a.header == b.header)) return false;
-
-    if (a.partitions.size() != b.partitions.size()) return false;
-
-    for(size_t i = 0; i < a.partitions.size(); ++i)
-        if (!(a.partitions[i] == b.partitions[i])) return false;
-
-    if (a.extents.size() != b.extents.size()) return false;
-
-    for(size_t i = 0; i < a.extents.size(); ++i)
-        if (!(a.extents[i] == b.extents[i])) return false;
-
-    if (a.groups.size() != b.groups.size()) return false;
-
-    for(size_t i = 0; i < a.groups.size(); ++i)
-        if (!(a.groups[i] == b.groups[i])) return false;
-
-    if (a.block_devices.size() != b.block_devices.size()) return false;
-
-    for(size_t i = 0; i < a.block_devices.size(); ++i)
-        if (!(a.block_devices[i] == b.block_devices[i])) return false;
-
-    return true;
-}
-
-std::ostream& metadataio::operator<<(std::ostream &s, const LpMetadataBlockDevice &m) {
-    s << m.first_logical_sector << " "
-        << m.alignment << " "
-        << m.alignment_offset << " "
-        << m.size << " "
-        << m.flags << " ";
-
-    for(int i = 0; i < 36; ++i)
-        s << static_cast<uint64_t>(m.partition_name[i]) << " ";
-
-    return s;
-}
-
-std::istream& metadataio::operator>>(std::istream &s, LpMetadataBlockDevice &m) {
-    uint64_t first_logical_sector, size, alignment, alignment_offset, flags, tmp;
-
-    s >> first_logical_sector >> alignment >> alignment_offset >> size >> flags;
-
-    for(int i = 0; i < 36; ++i) {
-        s >> tmp;
-        m.partition_name[i] = tmp;
-    }
-
-    m.first_logical_sector = first_logical_sector;
-    m.alignment = alignment;
-    m.alignment_offset = alignment_offset;
-    m.size = size;
-    m.flags = flags;
-
-    return s;
-}
-
-std::ostream& metadataio::operator<<(std::ostream &s, const LpMetadataPartitionGroup &m) {
-    s << m.flags << " "
-        << m.maximum_size << " ";
-
-    for(int i = 0; i < 36; ++i)
-        s << static_cast<uint64_t>(m.name[i]) << " ";
-
-    return s;
-}
-
-std::istream& metadataio::operator>>(std::istream &s, LpMetadataPartitionGroup &m) {
-    uint64_t flags, maximum_size, tmp;
-
-    s >> flags >> maximum_size;
-
-    for(int i = 0; i < 36; ++i) {
-        s >> tmp;
-        m.name[i] = tmp;
-    }
-
-    m.flags = flags;
-    m.maximum_size = maximum_size;
-
-    return s;
-}
-
-std::ostream& metadataio::operator<<(std::ostream &s, const LpMetadataExtent &m) {
-    return s << m.num_sectors << " "
-        << m.target_type << " "
-        << m.target_data << " "
-        << m.target_source << " ";
-}
-
-std::istream& metadataio::operator>>(std::istream &s, LpMetadataExtent &m) {
-    uint64_t num_sectors, target_data, target_type, target_source;
-
-    s >> num_sectors >> target_type >> target_data >> target_source;
-
-    m.num_sectors = num_sectors;
-    m.target_type = target_type;
-    m.target_data = target_data;
-    m.target_source = target_source;
-
-    return s;
-}
-
-std::ostream& metadataio::operator<<(std::ostream &s, const LpMetadataPartition &m) {
-    s << m.attributes << " "
-        << m.first_extent_index << " "
-        << m.num_extents << " "
-        << m.group_index << " ";
-
-    for(int i = 0; i < 36; ++i)
-        s << static_cast<uint64_t>(m.name[i]) << " ";
-
-    return s;
-}
-
-std::istream& metadataio::operator>>(std::istream &s, LpMetadataPartition &m) {
-    uint64_t attributes, first_extent_index, num_extents, group_index, tmp;
-
-    s >> attributes >> first_extent_index >> num_extents >> group_index;
-
-    for(int i = 0; i < 36; ++i) {
-        s >> tmp;
-        m.name[i] = tmp;
-    }
-
-    m.attributes = attributes;
-    m.first_extent_index = first_extent_index;
-    m.num_extents = num_extents;
-    m.group_index = group_index;
-
-    return s;
-}
-
-std::ostream& metadataio::operator<<(std::ostream &s, const LpMetadataTableDescriptor &m) {
-    return s << m.offset << " "
-        << m.num_entries << " "
-        << m.entry_size << " ";
-}
-
-std::istream& metadataio::operator>>(std::istream &s, LpMetadataTableDescriptor &m) {
-    uint64_t offset, num_entries, entry_size;
-
-    s >> offset >> num_entries >> entry_size;
-
-    m.offset = offset;
-    m.num_entries = num_entries;
-    m.entry_size = entry_size;
-
-    return s;
-}
-
-std::ostream& metadataio::operator<<(std::ostream &s, const LpMetadataHeader &m) {
-    s << m.magic << " "
-        << m.major_version << " "
-        << m.minor_version << " "
-        << m.header_size << " "
-        << m.tables_size << " "
-        << m.flags << " "
-        << m.partitions << " "
-        << m.extents << " "
-        << m.groups << " "
-        << m.block_devices << " ";
-
-    for(int i = 0; i < 32; ++i)
-        s << static_cast<uint64_t>(m.header_checksum[i]) << " ";
-
-    for(int i = 0; i < 32; ++i)
-        s << static_cast<uint64_t>(m.tables_checksum[i]) << " ";
-
-    for(int i = 0; i < 124; ++i)
-        s << static_cast<uint64_t>(m.reserved[i]) << " ";
-
-    return s;
-}
-
-std::istream& metadataio::operator>>(std::istream &s, LpMetadataHeader &m) {
-    uint64_t magic, header_size, tables_size, flags, major_version, minor_version, tmp;
-
-    s >> magic >> major_version >> minor_version
-      >> header_size >> tables_size >> flags
-      >> m.partitions >> m.extents >> m.groups
-      >> m.block_devices;
-
-    for(int i = 0; i < 32; ++i) {
-        s >> tmp;
-        m.header_checksum[i] = tmp;
-    }
-
-    for(int i = 0; i < 32; ++i) {
-        s >> tmp;
-        m.tables_checksum[i] = tmp;
-    }
-
-    for(int i = 0; i < 124; ++i) {
-        s >> tmp;
-        m.reserved[i] = tmp;
-    }
-
-    m.magic = magic;
-    m.header_size = header_size;
-    m.tables_size = tables_size;
-    m.flags = flags;
-    m.major_version = major_version;
-    m.minor_version = minor_version;
-
-    return s;
-}
-
-std::ostream& metadataio::operator<<(std::ostream &s, const LpMetadataGeometry &m) {
-    s << m.magic << " "
-        << m.struct_size << " "
-        << m.metadata_max_size << " "
-        << m.metadata_slot_count << " "
-        << m.logical_block_size << " ";
-
-    for(int i = 0; i < 32; ++i)
-        s << static_cast<uint64_t>(m.checksum[i]) << " ";
-
-   return s;
-}
-
-std::istream& metadataio::operator>>(std::istream &s, LpMetadataGeometry &m) {
-    uint64_t magic, struct_size, metadata_max_size, metadata_slot_count, logical_block_size, tmp;
-
-    s >> magic >> struct_size >> metadata_max_size
-      >> metadata_slot_count >> logical_block_size;
-
-    for(int i = 0; i < 32; ++i) {
-        s >> tmp;
-        m.checksum[i] = tmp;
-    }
-
-    m.magic = magic;
-    m.struct_size = struct_size;
-    m.metadata_max_size = metadata_max_size;
-    m.metadata_slot_count = metadata_slot_count;
-    m.logical_block_size = logical_block_size;
-
-   return s;
-}
-
-std::ostream& metadataio::operator<<(std::ostream &s, const android::fs_mgr::LpMetadata &m) {
-    s << m.geometry << " " << m.header << " ";
-
-    s << m.partitions.size() << " ";
-
-    for(auto &el : m.partitions)
-        s << el << " ";
-
-    s << m.extents.size() << " ";
-
-    for(auto &el : m.extents)
-        s << el << " ";
-
-    s << m.groups.size() << " ";
-
-    for(auto &el : m.groups)
-        s << el << " ";
-
-    s << m.block_devices.size() << " ";
-
-    for(auto &el : m.block_devices)
-        s << el << " ";
-
-    return s;
-}
-
-std::istream& metadataio::operator>>(std::istream &s, android::fs_mgr::LpMetadata &m) {
-    uint64_t n_partitions, n_extents, n_groups, n_devices;
-    LpMetadataPartition partition;
-    LpMetadataExtent extent;
-    LpMetadataPartitionGroup group;
-    LpMetadataBlockDevice block_device;
-
-    s >> m.geometry >> m.header >> n_partitions;
-
-    for(uint64_t i = 0; i < n_partitions; ++i) {
-        s >> partition;
-        m.partitions.push_back(partition);
-    }
-
-    s >> n_extents;
-
-    for(uint64_t i = 0; i < n_extents; ++i) {
-        s >> extent;
-        m.extents.push_back(extent);
-    }
-
-    s >> n_groups;
-
-    for(uint64_t i = 0; i < n_groups; ++i) {
-        s >> group;
-        m.groups.push_back(group);
-    }
-
-    s >> n_devices;
-
-    for(uint64_t i = 0; i < n_devices; ++i) {
-        s >> block_device;
-        m.block_devices.push_back(block_device);
-    }
-
-    return s;
-}
\ No newline at end of file
diff --git a/src/parse-dynparts/test/metadataio.h b/src/parse-dynparts/test/metadataio.h
deleted file mode 100644 (file)
index 28e14ca..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-/* Copyright (c) 2023 Samsung Electronics Co., Ltd.
- * SPDX-License-Identifier: MIT */
-
-#ifndef _METADATAIO_H_
-#define _METADATAIO_H_
-
-#include <liblp/liblp.h>
-#include <iostream>
-
-namespace metadataio {
-    bool operator==(const LpMetadataBlockDevice &a, const LpMetadataBlockDevice &b);
-    bool operator==(const LpMetadataPartitionGroup &a, const LpMetadataPartitionGroup &b);
-    bool operator==(const LpMetadataExtent &a, const LpMetadataExtent &b);
-    bool operator==(const LpMetadataPartition &a, const LpMetadataPartition &b);
-    bool operator==(const LpMetadataTableDescriptor &a, const LpMetadataTableDescriptor &b);
-    bool operator==(const LpMetadataGeometry &a, const LpMetadataGeometry &b);
-    bool operator==(const LpMetadataHeader &a, const LpMetadataHeader &b);
-    bool operator==(const android::fs_mgr::LpMetadata &a, const android::fs_mgr::LpMetadata &b);
-
-    std::ostream& operator<<(std::ostream &s, const LpMetadataBlockDevice &m);
-    std::ostream& operator<<(std::ostream &s, const LpMetadataPartitionGroup &m);
-    std::ostream& operator<<(std::ostream &s, const LpMetadataExtent &m);
-    std::ostream& operator<<(std::ostream &s, const LpMetadataPartition &m);
-    std::ostream& operator<<(std::ostream &s, const LpMetadataTableDescriptor &m);
-    std::ostream& operator<<(std::ostream &s, const LpMetadataGeometry &m);
-    std::ostream& operator<<(std::ostream &s, const LpMetadataHeader &m);
-    std::ostream& operator<<(std::ostream &s, const android::fs_mgr::LpMetadata &m);
-
-    std::istream& operator>>(std::istream &s, LpMetadataBlockDevice &m);
-    std::istream& operator>>(std::istream &s, LpMetadataPartitionGroup &m);
-    std::istream& operator>>(std::istream &s, LpMetadataExtent &m);
-    std::istream& operator>>(std::istream &s, LpMetadataPartition &m);
-    std::istream& operator>>(std::istream &s, LpMetadataTableDescriptor &m);
-    std::istream& operator>>(std::istream &s, LpMetadataGeometry &m);
-    std::istream& operator>>(std::istream &s, LpMetadataHeader &m);
-    std::istream& operator>>(std::istream &s, android::fs_mgr::LpMetadata &m);
-}
-
-#endif // _METADATAIO_H_
\ No newline at end of file
diff --git a/src/parse-dynparts/test/super_dump.cpp b/src/parse-dynparts/test/super_dump.cpp
deleted file mode 100644 (file)
index e61f2da..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-/* Copyright (c) 2023 Samsung Electronics Co., Ltd.
- * SPDX-License-Identifier: MIT */
-
-#include <iostream>
-#include <liblp/liblp.h>
-
-#include "metadataio.h"
-
-using namespace std;
-using namespace metadataio;
-
-int main(int argc, char **argv) {
-    if(argc != 2) {
-        cout << "Usage: " << argv[0] << " path_to_super" << endl;
-        return 1;
-    }
-
-    auto metadata = android::fs_mgr::ReadMetadata(argv[1], 0);
-
-    if (!metadata) {
-        cerr << "Failed to parse metadata from \"" << argv[1] << "\"" << endl;
-        return 1;
-    }
-
-    cout << *metadata << endl;
-
-    return 0;
-}
\ No newline at end of file
diff --git a/src/parse-dynparts/test/test.cpp b/src/parse-dynparts/test/test.cpp
deleted file mode 100644 (file)
index 5dce862..0000000
+++ /dev/null
@@ -1,940 +0,0 @@
-/* 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.
- *
- * 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.
- *
- * 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. */
-
-#include <iostream>
-#include <gtest/gtest.h>
-#include <string>
-#include "../lib.hpp"
-
-using namespace android::fs_mgr;
-
-#ifdef METADATA_IO
-#include <sstream>
-#include "metadataio.h"
-using namespace metadataio;
-
-void readMetadata(LpMetadata &m, std::string s) {
-    std::stringstream metadata_stream;
-    metadata_stream << s;
-    metadata_stream >> m;
-}
-#endif
-
-#define PART_SIZE 5 // MB
-
-void addPartitions(LpMetadata &metadata, int number_of_groups, int number_of_parts) {
-    LpMetadataPartition p;
-    LpMetadataExtent e;
-
-    for(int group = 0; group < number_of_groups; ++group) {
-        std::string group_suffix =  "_";
-        group_suffix += ('a' + group);
-
-        for(int part = 0; part < number_of_parts; ++part) {
-            int idx = group * number_of_parts + part;
-            p.attributes = 0;
-            p.first_extent_index = idx;
-            p.num_extents = 1;
-            p.group_index = group + 1;
-            std::string part_name = "part_" + std::to_string(part + 1) + group_suffix;
-            strncpy(p.name, part_name.c_str(), 36);
-
-            metadata.partitions.push_back(p);
-
-            e.num_sectors = 10240;
-            e.target_type = 0;
-            // add offset equall to idx * PART_SIZE as a number of 512B sectors (1024 * 1024 / 512)
-            e.target_data = 2048 + idx * PART_SIZE * 2048;
-            e.target_source = 0;
-
-            metadata.extents.push_back(e);
-        }
-    }
-}
-
-// Empty metadata
-TEST(ParseDynpartsTest, EmptyMetadata) {
-    LpMetadata metadata {};
-    std::ostringstream messages;
-    EXPECT_EQ(go(metadata, false, "test", messages), "");
-    EXPECT_EQ(messages.str(), "");
-
-    EXPECT_EQ(go(metadata, true, "test", messages), "");
-    EXPECT_EQ(messages.str(), "");
-}
-
-// 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";
-    readMetadata(metadata, metadata_str);
-#else
-   addPartitions(metadata, 1, 1);
-#endif
-
-    std::string output = "part_1_a,,,rw,0 10240 linear test 2048";
-    std::string output_list_tables = "part_1_a 0 10240 linear test 2048";
-    std::ostringstream messages;
-
-    EXPECT_EQ(go(metadata, false, "test", messages), output);
-    EXPECT_EQ(messages.str(), "");
-
-    EXPECT_EQ(go(metadata, true, "test", messages), output_list_tables);
-    EXPECT_EQ(messages.str(), "");
-}
-
-// 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  ";
-    readMetadata(metadata, metadata_str);
-#else
-    addPartitions(metadata, 1, 2);
-#endif
-
-    std::string output = "part_1_a,,,rw,0 10240 linear test 2048;part_2_a,,,rw,0 10240 linear test 12288";
-    std::string output_list_tables = "part_1_a 0 10240 linear test 2048\npart_2_a 0 10240 linear test 12288";
-    std::ostringstream messages;
-
-    EXPECT_EQ(go(metadata, false, "test", messages), output);
-    EXPECT_EQ(messages.str(), "");
-
-    EXPECT_EQ(go(metadata, true, "test", messages), output_list_tables);
-    EXPECT_EQ(messages.str(), "");
-}
-
-// 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  ";
-    readMetadata(metadata, metadata_str);
-#else
-    addPartitions(metadata, 1, 3);
-#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";
-    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";
-    std::ostringstream messages;
-
-    EXPECT_EQ(go(metadata, false, "test", messages), output);
-    EXPECT_EQ(messages.str(), "");
-
-    EXPECT_EQ(go(metadata, true, "test", messages), output_list_tables);
-    EXPECT_EQ(messages.str(), "");
-}
-
-// 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";
-    readMetadata(metadata, metadata_str);
-#else
-    addPartitions(metadata, 1, 4);
-#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";
-    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";
-    std::ostringstream messages;
-
-    EXPECT_EQ(go(metadata, false, "test", messages), output);
-    EXPECT_EQ(messages.str(), "");
-
-    EXPECT_EQ(go(metadata, true, "test", messages), output_list_tables);
-    EXPECT_EQ(messages.str(), "");
-}
-
-// 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";
-    readMetadata(metadata, metadata_str);
-#else
-    addPartitions(metadata, 2, 1);
-#endif
-
-    std::string output = "part_1_a,,,rw,0 10240 linear test 2048;part_1_b,,,rw,0 10240 linear test 12288";
-    std::string output_list_tables = "part_1_a 0 10240 linear test 2048\npart_1_b 0 10240 linear test 12288";
-    std::ostringstream messages;
-
-    EXPECT_EQ(go(metadata, false, "test", messages), output);
-    EXPECT_EQ(messages.str(), "");
-
-    EXPECT_EQ(go(metadata, true, "test", messages), output_list_tables);
-    EXPECT_EQ(messages.str(), "");
-}
-
-// 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";
-    readMetadata(metadata, metadata_str);
-#else
-    addPartitions(metadata, 2, 2);
-#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";
-    std::string output_list_tables = "part_1_a 0 10240 linear test 2048\npart_2_a 0 10240 linear test 12288\npart_1_b 0 10240 linear test 22528\npart_2_b 0 10240 linear test 32768";
-    std::ostringstream messages;
-
-    EXPECT_EQ(go(metadata, false, "test", messages), output);
-    EXPECT_EQ(messages.str(), "");
-
-    EXPECT_EQ(go(metadata, true, "test", messages), output_list_tables);
-    EXPECT_EQ(messages.str(), "");
-}
-
-// 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";
-    readMetadata(metadata, metadata_str);
-#else
-    addPartitions(metadata, 2, 3);
-#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";
-    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_1_b 0 10240 linear test 32768\npart_2_b 0 10240 linear test 43008\npart_3_b 0 10240 linear test 53248";
-    std::ostringstream messages;
-
-    EXPECT_EQ(go(metadata, false, "test", messages), output);
-    EXPECT_EQ(messages.str(), "");
-
-    EXPECT_EQ(go(metadata, true, "test", messages), output_list_tables);
-    EXPECT_EQ(messages.str(), "");
-}
-
-// 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";
-    readMetadata(metadata, metadata_str);
-#else
-    addPartitions(metadata, 2, 4);
-#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";
-    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_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";
-    std::ostringstream messages;
-
-    EXPECT_EQ(go(metadata, false, "test", messages), output);
-    EXPECT_EQ(messages.str(), "");
-
-    EXPECT_EQ(go(metadata, true, "test", messages), output_list_tables);
-    EXPECT_EQ(messages.str(), "");
-}
-
-// 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";
-    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;
-
-    metadata.extents[0].target_data = 12288;
-
-    metadata.extents[1].num_sectors = 2048;
-    metadata.extents[1].target_data = 2048;
-
-    metadata.extents[2].num_sectors = 2048;
-    metadata.extents[2].target_data = 4096;
-
-    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);
-#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::ostringstream messages;
-
-    EXPECT_EQ(go(metadata, false, "test", messages), output);
-    EXPECT_EQ(messages.str(), "");
-
-    EXPECT_EQ(go(metadata, true, "test", messages), output_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";
-    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);
-#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::ostringstream messages;
-
-    EXPECT_EQ(go(metadata, false, "test", messages), output);
-    EXPECT_EQ(messages.str(), "");
-
-    EXPECT_EQ(go(metadata, true, "test", messages), output_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";
-    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);
-#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::ostringstream messages;
-
-    EXPECT_EQ(go(metadata, false, "test", messages), output);
-    EXPECT_EQ(messages.str(), "");
-
-    EXPECT_EQ(go(metadata, true, "test", messages), output_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";
-    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);
-
-    strncpy(metadata.partitions[4].name, "part_1_a", 36);
-    metadata.partitions[4].group_index = 1;
-
-    strncpy(metadata.partitions[5].name, "part_3_a", 36);
-    metadata.partitions[5].group_index = 1;
-
-    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);
-
-    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);
-#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::ostringstream messages;
-
-    EXPECT_EQ(go(metadata, false, "test", messages), output);
-    EXPECT_EQ(messages.str(), "");
-
-    EXPECT_EQ(go(metadata, true, "test", messages), output_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";
-    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;
-
-    metadata.extents[4].num_sectors = 2048;
-    metadata.extents[4].target_data = 32768;
-
-    metadata.extents[5].num_sectors = 2048;
-    metadata.extents[5].target_data = 34816;
-
-    LpMetadataExtent e;
-    e.num_sectors = 6144;
-    e.target_type = 0;
-    e.target_data = 36864;
-    e.target_source = 0;
-
-    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);
-#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::ostringstream messages;
-
-    EXPECT_EQ(go(metadata, false, "test", messages), output);
-    EXPECT_EQ(messages.str(), "");
-
-    EXPECT_EQ(go(metadata, true, "test", messages), output_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";
-    readMetadata(metadata, metadata_str);
-#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);
-#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(), "");
-
-    EXPECT_EQ(go(metadata, true, "test", messages), output_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";
-    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);
-#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::ostringstream messages;
-
-    EXPECT_EQ(go(metadata, false, "test", messages), output);
-    EXPECT_EQ(messages.str(), "");
-
-    EXPECT_EQ(go(metadata, true, "test", messages), output_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";
-    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);
-#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::ostringstream messages;
-
-    EXPECT_EQ(go(metadata, false, "test", messages), output);
-    EXPECT_EQ(messages.str(), "");
-
-    EXPECT_EQ(go(metadata, true, "test", messages), output_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";
-    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::ostringstream messages;
-
-    EXPECT_EQ(go(metadata, false, "test", messages), output);
-    EXPECT_EQ(messages.str(), "");
-
-    EXPECT_EQ(go(metadata, true, "test", messages), output_list_tables);
-    EXPECT_EQ(messages.str(), "");
-}
\ No newline at end of file