--- /dev/null
+---
+Language: Cpp
+BasedOnStyle: Google
+...
--- /dev/null
+{
+ // 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": []
+}
--- /dev/null
+{
+ "C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools",
+ "cmake.configureOnOpen": true
+}
--- /dev/null
+cmake_minimum_required(VERSION 3.10)
+
+project(parse-android-dynparts VERSION 0.1)
+
+set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD_REQUIRED True)
+
+add_subdirectory(liblp)
+
+add_executable(parse-android-dynparts main.cpp)
+target_compile_options(parse-android-dynparts PRIVATE -Wall -Wextra -pedantic)
+target_link_libraries(parse-android-dynparts PRIVATE lp)
--- /dev/null
+ 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.
--- /dev/null
+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-android-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
+ ```
--- /dev/null
+add_library(lp reader.cpp utility.cpp)
+
+target_include_directories(lp PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
+target_compile_options(lp PRIVATE -Wall -Wextra -pedantic)
+
+find_package(OpenSSL REQUIRED)
+target_link_libraries(lp PRIVATE OpenSSL::Crypto)
--- /dev/null
+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/
--- /dev/null
+//
+// 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
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/*
+ * 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)) {
+ return nullptr;
+ }
+ if (slot_number >= geometry.metadata_slot_count) {
+ LERROR << __PRETTY_FUNCTION__ << " invalid metadata slot number";
+ 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)) {
+ return nullptr;
+ }
+ 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
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/*
+ * 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 "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]) {
+ SHA256_CTX c;
+ SHA256_Init(&c);
+ SHA256_Update(&c, data, length);
+ SHA256_Final(out, &c);
+}
+
+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
--- /dev/null
+/*
+ * 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
--- /dev/null
+#include <liblp/liblp.h>
+
+#include <iostream>
+#include <sstream>
+#include <string_view>
+
+using namespace android::fs_mgr;
+
+using std::cerr;
+using std::cout;
+using std::endl;
+
+constexpr std::string_view kDmPrefix = "dynpart-";
+
+int main(int argc, char* argv[]) {
+ if (argc != 2) {
+ cerr << "Usage: dmsetup create --concise \"$(" << argv[0] << " device)\""
+ << endl;
+ return 1;
+ }
+
+ auto metadata = ReadMetadata(argv[1], 0);
+ if (!metadata) {
+ cerr << "Failed to parse metadata from \"" << argv[1] << "\"" << endl;
+ return 1;
+ }
+
+ std::string table;
+
+ // Code structure taken from Android's system/core/fs_mgr/fs_mgr_dm_linear.cpp
+ for (auto partition : metadata->partitions) {
+ if (!partition.num_extents) {
+ cerr << "Skipping zero-length logical partition: "
+ << GetPartitionName(partition) << endl;
+ continue;
+ }
+ if (partition.attributes & LP_PARTITION_ATTR_DISABLED) {
+ cerr << "Skipping disabled partition: " << GetPartitionName(partition)
+ << endl;
+ continue;
+ }
+
+ std::ostringstream line;
+ bool read_only = partition.attributes & LP_PARTITION_ATTR_READONLY;
+ line << kDmPrefix << 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) {
+ cerr << "This utility does not yet support multiple block devices"
+ << endl;
+ return 1;
+ }
+
+ line << "," << sector << " " << extent.num_sectors << " linear "
+ << argv[1] << " " << extent.target_data;
+ break;
+ }
+ default:
+ cerr << "Unknown target type in metadata: " << extent.target_type
+ << endl;
+ return false;
+ }
+ sector += extent.num_sectors;
+ }
+
+ if (!table.empty()) table += ";";
+ table += line.str();
+ }
+
+ cout << table << endl;
+
+ return 0;
+}