|  | // | 
|  | // 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 "liblp.h" | 
|  | #include "partition_opener.h" | 
|  |  | 
|  | namespace android { | 
|  | namespace fs_mgr { | 
|  |  | 
|  | class LinearExtent; | 
|  |  | 
|  | // By default, partitions are aligned on a 1MiB boundary. | 
|  | static const uint32_t kDefaultPartitionAlignment = 1024 * 1024; | 
|  | static const uint32_t kDefaultBlockSize = 4096; | 
|  |  | 
|  | // 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; } | 
|  |  | 
|  | 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_; | 
|  | }; | 
|  |  | 
|  | // 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; } | 
|  |  | 
|  | 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_; } | 
|  |  | 
|  | 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; | 
|  | }; | 
|  |  | 
|  | class PartitionGroup final { | 
|  | friend class MetadataBuilder; | 
|  |  | 
|  | public: | 
|  | explicit PartitionGroup(const std::string& 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(const std::string& name, const std::string& 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_; } | 
|  | const std::vector<std::unique_ptr<Extent>>& extents() const { return extents_; } | 
|  | uint64_t size() const { return size_; } | 
|  |  | 
|  | private: | 
|  | void ShrinkTo(uint64_t aligned_size); | 
|  | void set_group_name(const std::string& 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_; | 
|  | }; | 
|  |  | 
|  | class MetadataBuilder { | 
|  | public: | 
|  | // Construct an empty logical partition table builder given the specified | 
|  | // map of partitions that are available for storing logical partitions. | 
|  | // | 
|  | // At least one partition in the list must be the "super" device, where | 
|  | // metadata will be stored. | 
|  | // | 
|  | // If the parameters would yield invalid metadata, nullptr is returned. This | 
|  | // could happen if the super device is too small to store all required | 
|  | // metadata. | 
|  | static std::unique_ptr<MetadataBuilder> New(const std::vector<BlockDeviceInfo>& block_devices, | 
|  | const std::string& super_partition, | 
|  | uint32_t metadata_max_size, | 
|  | uint32_t metadata_slot_count); | 
|  |  | 
|  | // Import an existing table for modification. This reads metadata off the | 
|  | // given block device and imports it. It also adjusts alignment information | 
|  | // based on run-time values in the operating system. | 
|  | static std::unique_ptr<MetadataBuilder> New(const IPartitionOpener& opener, | 
|  | const std::string& super_partition, | 
|  | uint32_t slot_number); | 
|  |  | 
|  | // Same as above, but use the default PartitionOpener. | 
|  | static std::unique_ptr<MetadataBuilder> New(const std::string& super_partition, | 
|  | uint32_t slot_number); | 
|  |  | 
|  | // This is when performing an A/B update. The source partition must be a | 
|  | // super partition. On a normal device, the metadata for the source slot | 
|  | // is imported and the target slot is ignored. On a retrofit device, the | 
|  | // metadata may not have the target slot's devices listed yet, in which | 
|  | // case, it is automatically upgraded to include all available block | 
|  | // devices. | 
|  | static std::unique_ptr<MetadataBuilder> NewForUpdate(const IPartitionOpener& opener, | 
|  | const std::string& source_partition, | 
|  | uint32_t source_slot_number, | 
|  | uint32_t target_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, | 
|  | const IPartitionOpener* opener = nullptr); | 
|  |  | 
|  | // Helper function for a single super partition, for tests. | 
|  | static std::unique_ptr<MetadataBuilder> New(const BlockDeviceInfo& device_info, | 
|  | uint32_t metadata_max_size, | 
|  | uint32_t metadata_slot_count) { | 
|  | return New({device_info}, device_info.partition_name, metadata_max_size, | 
|  | metadata_slot_count); | 
|  | } | 
|  |  | 
|  | // Wrapper around New() with a BlockDeviceInfo that only specifies a device | 
|  | // size. This is a convenience method for tests. | 
|  | static std::unique_ptr<MetadataBuilder> New(uint64_t blockdev_size, uint32_t metadata_max_size, | 
|  | uint32_t metadata_slot_count) { | 
|  | BlockDeviceInfo device_info(LP_METADATA_DEFAULT_PARTITION_NAME, blockdev_size, 0, 0, | 
|  | kDefaultBlockSize); | 
|  | return New(device_info, metadata_max_size, metadata_slot_count); | 
|  | } | 
|  |  | 
|  | // Used by the test harness to override whether the device is "A/B". | 
|  | static void OverrideABForTesting(bool ab_device); | 
|  |  | 
|  | // 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(const std::string& 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(const std::string& name, const std::string& 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(const std::string& name); | 
|  |  | 
|  | // Find a partition by name. If no partition is found, nullptr is returned. | 
|  | Partition* FindPartition(const std::string& name); | 
|  |  | 
|  | // Find a group by name. If no group is found, nullptr is returned. | 
|  | PartitionGroup* FindGroup(const std::string& name); | 
|  |  | 
|  | // Add a predetermined extent to a partition. | 
|  | bool AddLinearExtent(Partition* partition, const std::string& block_device, | 
|  | uint64_t num_sectors, uint64_t physical_sector); | 
|  |  | 
|  | // 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. | 
|  | bool ResizePartition(Partition* partition, uint64_t requested_size); | 
|  |  | 
|  | // Return the list of partitions belonging to a group. | 
|  | std::vector<Partition*> ListPartitionsInGroup(const std::string& 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, const std::string& 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); | 
|  |  | 
|  | // Amount of space that can be allocated to logical partitions. | 
|  | uint64_t AllocatableSpace() const; | 
|  | uint64_t UsedSpace() const; | 
|  |  | 
|  | // 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(const std::string& group_name); | 
|  |  | 
|  | // Set the LP_METADATA_AUTO_SLOT_SUFFIXING flag. | 
|  | void SetAutoSlotSuffixing(); | 
|  |  | 
|  | // If set, checks for slot suffixes will be ignored internally. | 
|  | void IgnoreSlotSuffixing(); | 
|  |  | 
|  | bool GetBlockDeviceInfo(const std::string& partition_name, BlockDeviceInfo* info) const; | 
|  | bool UpdateBlockDeviceInfo(const std::string& partition_name, const BlockDeviceInfo& info); | 
|  |  | 
|  | // Attempt to preserve the named partitions from an older metadata. If this | 
|  | // is not possible (for example, the block device list has changed) then | 
|  | // false is returned. | 
|  | bool ImportPartitions(const LpMetadata& metadata, const std::set<std::string>& partition_names); | 
|  |  | 
|  | // Return true if a block device is found, else false. | 
|  | bool HasBlockDevice(const std::string& partition_name) const; | 
|  |  | 
|  | private: | 
|  | MetadataBuilder(); | 
|  | MetadataBuilder(const MetadataBuilder&) = delete; | 
|  | MetadataBuilder(MetadataBuilder&&) = delete; | 
|  | MetadataBuilder& operator=(const MetadataBuilder&) = delete; | 
|  | MetadataBuilder& operator=(MetadataBuilder&&) = delete; | 
|  | bool Init(const std::vector<BlockDeviceInfo>& block_devices, const std::string& super_partition, | 
|  | uint32_t metadata_max_size, uint32_t metadata_slot_count); | 
|  | bool Init(const LpMetadata& metadata); | 
|  | bool GrowPartition(Partition* partition, uint64_t aligned_size); | 
|  | void ShrinkPartition(Partition* partition, uint64_t aligned_size); | 
|  | uint64_t AlignSector(const LpMetadataBlockDevice& device, uint64_t sector) const; | 
|  | uint64_t TotalSizeOfGroup(PartitionGroup* group) const; | 
|  | bool UpdateBlockDeviceInfo(size_t index, const BlockDeviceInfo& info); | 
|  | bool FindBlockDeviceByName(const std::string& partition_name, uint32_t* index) const; | 
|  | bool ValidatePartitionSizeChange(Partition* partition, uint64_t old_size, uint64_t new_size); | 
|  | void ImportExtents(Partition* dest, const LpMetadata& metadata, | 
|  | const LpMetadataPartition& source); | 
|  | bool ImportPartition(const LpMetadata& metadata, const LpMetadataPartition& source); | 
|  | bool IsABDevice() const; | 
|  | bool IsRetrofitDevice() const; | 
|  | bool ValidatePartitionGroups() const; | 
|  |  | 
|  | 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::vector<Interval> GetFreeRegions() const; | 
|  | void ExtentsToFreeList(const std::vector<Interval>& extents, | 
|  | std::vector<Interval>* free_regions) const; | 
|  | std::vector<Interval> PrioritizeSecondHalfOfSuper(const std::vector<Interval>& free_list); | 
|  |  | 
|  | static bool sABOverrideValue; | 
|  | static bool sABOverrideSet; | 
|  |  | 
|  | 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_; | 
|  | bool ignore_slot_suffixing_; | 
|  | }; | 
|  |  | 
|  | // Read BlockDeviceInfo for a given block device. This always returns false | 
|  | // for non-Linux operating systems. | 
|  | bool GetBlockDeviceInfo(const std::string& block_device, BlockDeviceInfo* device_info); | 
|  |  | 
|  | }  // namespace fs_mgr | 
|  | }  // namespace android | 
|  |  | 
|  | #endif /* LIBLP_METADATA_BUILDER_H */ |