原文链接:mp.weixin.qq.com/s/tMNsKgNfy…
Android在A/B更新机制的基础上演进出了Virtual A/B更新机制,以减少升级过程中的磁盘空间开销。
Virtual A/B 不像 legacy A/B 一样在动态分区上真实地分配了a/b两份空间。而是将升级增量写入快照,然后在确认启动成功后合并到基本分区中。Virtual A/B 使用了 Android 定制的快照格式。这使得快照可以被压缩并最大限度地减少磁盘空间的使用。在全量 OTA 中,通过压缩,快照大小减少了约 45%,而差分 OTA 快照大小减少了约 55%。
接下来从快照如何创建的角度来探索Virtual A/B的升级原理。
快照分区的创建
super分区的数据格式
super分区的layout:
0x0000 +--------------------+
| reserved |
0x1000 +--------------------+
| primary Geometry |
0x2000 +--------------------+
| backup Geometry |
0x3000 +--------------------+
| primary Metadata |
0x13000 +--------------------+
| backup Metadata |
0x23000 +--------------------+
| backup Metadata |
0x33000 +--------------------+
| backup Metadata |
0x43000 +--------------------+
| |
| |
0x100000 +--------------------+
| system_a |
+--------------------+
| system_ext_a |
+--------------------+
| vendor_a |
+--------------------+
| ...... other |
| Logical Partitions |
期中super分区开头预留的4096空间是为了避免创建意外的引导区
system/core/fs_mgr/liblp/include/liblp/metadata_format.h:
/* Amount of space reserved at the start of every super partition to avoid
* creating an accidental boot sector.
*/
#define LP_PARTITION_RESERVED_BYTES 4096
定义super分区中metadata结构详细信息在 system/core/fs_mgr/liblp/include/liblp/metadata_format.h
我们找个super.img分析一下。
先将稀疏矩阵格式的super.img转化为普通img:
$ simg2img super.img super_orig.img
然后使用xxd命令, 即可看到super的primary Geometry
$ xxd -l 8192 super_orig.img
0000000: 0000 0000 0000 0000 0000 0000 0000 0000 ................
........开头预留的4096都是0
从0x1000开始就是primary Geometry
0001000: 6744 6c61 3400 0000 12ff 55f0 aba7 b506 gDla4.....U.....
0001010: f25c b5da 5dca 0934 4234 e8df 1d9c 93ae ...]..4B4......
0001020: 82a4 99d9 8019 467e 0000 0100 0300 0000 ......F~........
0001030: 0010 0000 0000 0000 0000 0000 0000 0000 ................
0001040: 0000 0000 0000 0000 0000 0000 0000 0000 ................
........都是0
读取primary Metadata数据: GetPrimaryMetadataOffset() = LP_PARTITION_RESERVED_BYTES + (LP_METADATA_GEOMETRY_SIZE * 2) + geometry.metadata_max_size * slot_number = 4096 + 4096 * 2 + 0x10000 * slot_number
Metadata数据格式:
* +-----------------------------------------+
* | Header data - fixed size |
* +-----------------------------------------+
* | Partition table - variable size |
* +-----------------------------------------+
* | Partition table extents - variable size |
* +-----------------------------------------+
* | Partition groups - variable size |
* +-----------------------------------------+
* | Block devices - variable size |
* +-----------------------------------------+
期中Header data数据结构为LpMetadataHeader:
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;
Header data的二进制数据如下:
$ xxd -s 12288 -l 65536 super_orig.img
0003000: 3050 4c41 0a00 0200 0001 0000 4bda 5dea 0PLA........K.].
0003010: ca67 6075 27fe fa53 8940 1311 afe8 58da .g`u'..S.@....X.
0003020: ec28 ca64 5f4a 4f87 5148 de07 d003 0000 .(.d_JO.QH......
0003030: b11f 2677 b804 208b 59e0 d78a 6bd2 65aa ..&w.. .Y...k.e.
0003040: 4ee2 d8d5 4d64 768e 0e34 63e3 0d03 a8f6 N...Mdv..4c.....
0003050: 0000 0000 0c00 0000 3400 0000 7002 0000 ........4...p...
0003060: 0600 0000 1800 0000 0003 0000 0300 0000 ................
0003070: 3000 0000 9003 0000 0100 0000 4000 0000 0...........@...
0003080: 0100 0000 0000 0000 0000 0000 0000 0000 ................
0x414C5030为LpMetadataHeader.magic
从0x3050开始,是
/* 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;
的数据。
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;
解析得:
LpMetadataTableDescriptor partitions:
uint32_t offset = 0 // LpMetadataHeader 大小为256, 所以分区表从0x3100开始
uint32_t num_entries = 0x0c = 12 //12个分区
uint32_t entry_size = 0x34
LpMetadataTableDescriptor extents
uint32_t offset = 0x270 // LpMetadataHeader 大小为256, 所以extents表从0x3370开始
uint32_t num_entries = 0x06 = 6 //6个extent
uint32_t entry_size = 0x18 //每个extent条目的大小
LpMetadataTableDescriptor groups
uint32_t offset = 0x300 // LpMetadataHeader 大小为256, 所以groups表从0x3400开始
uint32_t num_entries = 0x03 = 3 //3个group
uint32_t entry_size = 0x30 //每个group条目的大小
LpMetadataTableDescriptor block_devices
uint32_t offset = 0x390 // LpMetadataHeader 大小为256, 所以block_devices表从0x3490开始
uint32_t num_entries = 0x01 = 1 //1个block_device
uint32_t entry_size = 0x40 //每个block_device条目的大小
接着从0x3100开始解析分区表:
分区表 LpMetadataPartition, 大小0x34:
0003100: 7379 7374 656d 5f61 0000 0000 0000 0000 system_a........
0003110: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0003120: 0000 0000 0100 0000 0000 0000 0100 0000 ................
0003130: 0100 0000 7379 7374 656d 5f62 0000 0000 ....system_b....
0003140: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0003150: 0000 0000 0000 0000 0100 0000 0100 0000 ................
0003160: 0000 0000 0200 0000 7379 7374 656d 5f65 ........system_e
name[36] attributes first_extent_index num_extents group_index
system_a 01 0 01 01
system_b 01 01 0 02
system_ext_a 01 01 01 01
system_ext_b 01 02 0 02
vendor_a 01 02 01 01
......
从0x3370开始解析extents表:
0003370: e8e0 1100 0000 0000 0000 0000 0008 0000
0003380: 0000 0000 0000 0000 d0d2 0a00 0000 0000
0003390: 0000 0000 00f0 1100 0000 0000 0000 0000
00033a0: 3057 1600 0000 0000 0000 0000 00c8 1c00
00033b0: 0000 0000 0000 0000 788a 0400 0000 0000
00033c0: 0000 0000 0020 3300 0000 0000 0000 0000
00033d0: c04c 0700 0000 0000 0000 0000 00b0 3700
00033e0: 0000 0000 0000 0000 d043 0000 0000 0000
00033f0: 0000 0000 0000 3f00 0000 0000 0000 0000
LpMetadataExtent:
num_sectors target_type target_data target_source
0x11e0e8 0 - LINEAR 0x800 0
0x0ad2d0 0 - LINEAR 0x11f000 0
0x165730 0 - LINEAR 0x1cc800 0
0x048a78 0 - LINEAR 0x332000 0
0x074cc0 0 - LINEAR 0x37b000 0
0x0043d0 0 - LINEAR 0x3f0000 0
0x3400开始解析groups表:
0003400: 6465 6661 756c 7400 0000 0000 0000 0000 default.........
0003410: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0003420: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0003430: 6772 6f75 705f 756e 6973 6f63 5f61 0000 group_unisoc_a..
0003440: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0003450: 0000 0000 0000 0000 0000 c05d 0100 0000 ...........]....
0003460: 6772 6f75 705f 756e 6973 6f63 5f62 0000 group_unisoc_b..
0003470: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0003480: 0000 0000 0000 0000 0000 c05d 0100 0000 ...........]....
LpMetadataPartitionGroup:
char name[36] flags maximum_size
default 0 0
group_unisoc_a 0 0x015dc00000
group_unisoc_b 0 0x015dc00000
0x3490开始解析block_devices表:
0003490: 0008 0000 0000 0000 0000 1000 0000 0000 ................
00034a0: 0000 005e 0100 0000 7375 7065 7200 0000 ...^....super...
00034b0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00034c0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
LpMetadataBlockDevice:
first_logical_sector = 0x0000000000000800
alignment = 0x00100000
alignment_offset = 0x0
size = 0x015e000000
partition_name[36] = super
flags = 0
由LpMetadataPartition.first_extent_index从extents表找到对应的extent, 然后根据找到的LpMetadataExtent.target_data和LpMetadataExtent.num_sectors即可从super分区中找到对应的分区数据。
如system_a的first_extent_index = 0, num_extents = 1 所以对应的extent是
num_sectors target_type target_data target_source
0x11e0e8 0 - LINEAR 0x800 0
0x800 = 2048 0x11e0e8 = 1171688 通过命令
$ dd if=super_orig.img of=system_from_super.img skip=2048 ibs=512 obs=512 count=1171688
即可导出system.img
COW(copy-on-write)分区的创建
COW分区用于存储新系统和老系统的差分数据(新系统-老系统=COW)。COW内容默认会在super分区中创建临时的逻辑分区来进行存储, 而在super分区空间不够用时,就会在/data下创建COW文件。
所以可以根据需求适当减小super分区的大小,以增大date分区的大小,但需要确保在更新系统时,data分区有足够的空间用于存储COW文件。或者,设备只需确保 super 足够大,即可保证在系统升级时绝不需要 /data。参考 Size the super partition
现以都在super分区上创建COW为例分析COW的创建过程。
计算super的空闲空间,并分配给COW
system/core/fs_mgr/libsnapshot/snapshot.cpp:
Return SnapshotManager::CreateUpdateSnapshots(const DeltaArchiveManifest& manifest) {
// 系统当前用的metadata
auto current_metadata = MetadataBuilder::New(opener, current_super, current_slot);
// 新系统的metadata
//将current_metadata中的_a分区名字改为'_b',并赋值给target_metadata里的partitions, 原有的_b分区信息舍弃掉
auto target_metadata =
MetadataBuilder::NewForUpdate(opener, current_super, current_slot, target_slot);
// Delete partitions with target suffix in |current_metadata|. Otherwise,
// partition_cow_creator recognizes these left-over partitions as used space.
// 从current_metadata中删掉'_b'的grop和partitions
for (const auto& group_name : current_metadata->ListGroups()) {
if (android::base::EndsWith(group_name, target_suffix)) {
current_metadata->RemoveGroupAndPartitions(group_name);
}
}
// 用来构建COW分区的参数
PartitionCowCreator cow_creator{
.target_metadata = target_metadata.get(),
.target_suffix = target_suffix,
.target_partition = nullptr,
.current_metadata = current_metadata.get(),
.current_suffix = current_suffix,
.update = nullptr,
.extra_extents = {},
.compression_enabled = use_compression,
.compression_algorithm = compression_algorithm,
};
auto ret = CreateUpdateSnapshotsInternal(lock.get(), manifest, &cow_creator, &created_devices,
&all_snapshot_status);
ret = InitializeUpdateSnapshots(lock.get(), target_metadata.get(),
exported_target_metadata.get(), target_suffix,
all_snapshot_status);
//更新super上的分区表
// [liblp]Updated logical partition table at slot 1 on device super
if (!UpdatePartitionTable(opener, device_->GetSuperDevice(target_slot),
*exported_target_metadata, target_slot)) {
LOG(ERROR) << "Cannot write target metadata";
return Return::Error();
}
}
在CreateUpdateSnapshots里有2个关键参数current_metadata和target_metadata, 它们是LpMetadata类型的数据,分别存储了当前系统和新系统的分区表信息。
Return SnapshotManager::CreateUpdateSnapshotsInternal(
LockedFile* lock, const DeltaArchiveManifest& manifest, PartitionCowCreator* cow_creator,
AutoDeviceList* created_devices,
std::map<std::string, SnapshotStatus>* all_snapshot_status) {
for (auto* target_partition : ListPartitionsWithSuffix(target_metadata, target_suffix)) {
// Compute the device sizes for the partition.
// 计算super上的剩余空间
auto cow_creator_ret = cow_creator->Run();
LOG(INFO) << "For partition " << target_partition->name()
<< ", device size = " << cow_creator_ret->snapshot_status.device_size()
<< ", snapshot size = " << cow_creator_ret->snapshot_status.snapshot_size()
<< ", cow partition size = "
<< cow_creator_ret->snapshot_status.cow_partition_size()
<< ", cow file size = " << cow_creator_ret->snapshot_status.cow_file_size();
// Store these device sizes to snapshot status file.
if (!CreateSnapshot(lock, cow_creator, &cow_creator_ret->snapshot_status)) {
return Return::Error();
}
// Create the COW partition. That is, use any remaining free space in super partition before
// creating the COW images.
if (cow_creator_ret->snapshot_status.cow_partition_size() > 0) {
if (!target_metadata->ResizePartition(
cow_partition, cow_creator_ret->snapshot_status.cow_partition_size(),
cow_creator_ret->cow_partition_usable_regions)) {
LOG(ERROR) << "Cannot create COW partition on metadata with size "
<< cow_creator_ret->snapshot_status.cow_partition_size();
return Return::Error();
}
// Only the in-memory target_metadata is modified; nothing to clean up if there is an
// error in the future.
}
}
LOG(INFO) << "Allocating CoW images.";
for (auto&& [name, snapshot_status] : *all_snapshot_status) {
// Create the backing COW image if necessary.
if (snapshot_status.cow_file_size() > 0) {
auto ret = CreateCowImage(lock, name);
if (!ret.is_ok()) return AddRequiredSpace(ret, *all_snapshot_status);
}
LOG(INFO) << "Successfully created snapshot for " << name;
}
}
期中CreateUpdateSnapshotsInternal函数里先执行cow_creator->Run()使用current_metadata和target_metadata里的分区表信息计算出super上的空闲空间。
system/core/fs_mgr/libsnapshot/partition_cow_creator.cpp
std::optional<PartitionCowCreator::Return> PartitionCowCreator::Run() {
// Being the COW partition virtual, its size doesn't affect the storage
// memory that will be occupied by the target.
// The actual storage space is affected by the COW file, whose size depends
// on the chunks that diverged between |current| and |target|.
// If the |target| partition is bigger than |current|, the data that is
// modified outside of |current| can be written directly to |current|.
// This because the data that will be written outside of |current| would
// not invalidate any useful information of |current|, thus:
// - if the snapshot is accepted for merge, this data would be already at
// the right place and should not be copied;
// - in the unfortunate case of the snapshot to be discarded, the regions
// modified by this data can be set as free regions and reused.
// Compute regions that are free in both current and target metadata. These are the regions
// we can use for COW partition.
auto target_free_regions = target_metadata->GetFreeRegions();
auto current_free_regions = current_metadata->GetFreeRegions();
auto free_regions = Interval::Intersect(target_free_regions, current_free_regions);
uint64_t free_region_length = 0;
for (const auto& interval : free_regions) {
free_region_length += interval.length();
}
free_region_length *= kSectorSize;
LOG(INFO) << "Remaining free space for COW: " << free_region_length << " bytes";
}
这里需要注意的是,GetFreeRegions在计算空闲extent时,地址对齐用的是524288,而不是super分区里block_devices表里的alignment = 0x00100000。为啥?因为从kernel去获取alignment了,代码如下:
system/core/fs_mgr/liblp/builder.cpp
std::unique_ptr<MetadataBuilder> MetadataBuilder::New(const LpMetadata& metadata,
const IPartitionOpener* opener) {
BlockDeviceInfo device_info;
if (opener->GetInfo(partition_name, &device_info)) {
builder->UpdateBlockDeviceInfo(i, device_info);
}
}
bool MetadataBuilder::UpdateBlockDeviceInfo(size_t index, const BlockDeviceInfo& device_info) {
LpMetadataBlockDevice& block_device = block_devices_[index];
// The kernel does not guarantee these values are present, so we only
// replace existing values if the new values are non-zero.
if (device_info.alignment) {
block_device.alignment = device_info.alignment;
}
}
system/core/fs_mgr/liblp/partition_opener.cpp
bool PartitionOpener::GetInfo(const std::string& partition_name, BlockDeviceInfo* info) const {
std::string path = GetPartitionAbsolutePath(partition_name);
return GetBlockDeviceInfo(path, info);
}
bool GetBlockDeviceInfo(const std::string& block_device, BlockDeviceInfo* device_info) {
#if defined(__linux__)
unique_fd fd = GetControlFileOrOpen(block_device.c_str(), O_RDONLY);
ioctl(fd, BLKIOMIN, &device_info->alignment)
}
current_metadata和target_metadata里的分区表如下, 图中地址是以sector为单位:
经过cow_creator->Run和target_metadata->ResizePartition后, 在super上分配了COW分区的空间如下:
创建好的COW设备会map到dm-user, 如:
Mapped COW device for system_b at /dev/block/dm-12
但在升级过程中这些COW设备会被多次unmap和重新map
使用快照映射分区
OTA升级流程来到了往分区写升级数据的步骤,启用了Virtual A/B Compression功能的分区会走以下代码:
VABCPartitionWriter::Init()
DynamicPartitionControlAndroid::OpenCowWriter()
SnapshotManager::OpenSnapshotWriter()
//unmap 快照分区
UnmapPartitionWithSnapshot()
MapPartitionWithSnapshot(SnapshotContext::Update)
//创建base设备: odm_b-base
CreateLogicalPartition()
//创建cow设备: odm_b-cow
MapCowDevices()
if (context == SnapshotContext::Update && live_snapshot_status->compression_enabled()) {
// 还没到启动snapuserd的时候,在这里退出
// Stop here, we can't run dm-user yet, the COW isn't built.
LOG(ERROR) << "Stop here, we can't run dm-user yet, the COW isn't built.";
created_devices.Release();
return true;
}
以odm_b为例,MapPartitionWithSnapshot()后,odm_b-base, odm_b-cow 2个分区对应的sector地址如下:
odm_b-base: 0x37B000 --- 0x3EFCC0
odm_b-cow: 0x6FF400 --- 0x766D88
odm_b-base,odm_b-cow分配的空间与之前的保持不变
odm_b-base, odm_b-cow的块设备路径分别如下:
odm_b-base: /dev/block/dm-12
odm_b-cow: /dev/block/dm-13
在vabc_partition_writer中, 会往/dev/block/dm-13也就是COW设备写压缩过的升级数据,就因为在vabc_partition_writer中对数据做了压缩处理,所以减慢了OTA升级速度,制作OTA包时加入--disable_vabc参数可以不对升级数据做压缩,加过升级速度。 耗时的DownloadAction执行完后,接着就是做hash校验的FilesystemVerifierAction了,在FilesystemVerifierAction又对快照分区做了多次的unmap和map操作:
FilesystemVerifierAction:
PerformAction()
StartPartitionHashing()
InitializeFdVABC()
dynamic_control_->UnmapAllPartitions();
dynamic_control_->MapAllPartitions();
DynamicPartitionControlAndroid:
MapAllPartitions()
snapshot_->MapAllSnapshots()
SnapshotManager:
MapAllSnapshots()
UnmapPartitionWithSnapshot()
MapPartitionWithSnapshot(SnapshotContext::Mount)
//创建base设备: odm_b-base
CreateLogicalPartition()
//创建cow设备: odm_b-cow
MapCowDevices()
//创建source设备: odm_b-src
MapSourceDevice()
//将odm_b-base, odm_b-cow, odm_b-src三个设备传给snapuserd
//以odm_b为名,odm_b-base的device size为参数创建dm-user设备
MapDmUserCow(lock, name, cow_path, source_device_path, base_path, remaining_time,
&new_cow_device))
// Snapuserd还没启动的话,就启动Snapuserd
EnsureSnapuserdConnected()
// 往Snapuserd发送'init'命令,创建dm-user '路由表', 对odm_b的I/O操作,将通过Snapuserd路由到odm_b-base, odm_b-cow, odm_b-src
snapuserd_client_->InitDmUserCow
// 往Snapuserd发送'start'命令
snapuserd_client_->AttachDmUser
以odm_b为例,MapPartitionWithSnapshot后,odm_b-base, odm_b-cow, odm_b-src 3个分区对应的sector地址如下:
odm_b-base: 0x37B000 --- 0x3EFCC0
odm_b-cow: 0x6FF400 --- 0x766D88
odm_b-src: 0x37B000 --- 0x3EFCC0
odm_b-base,odm_b-cow分配的空间与之前的保持不变, odm_b-src = odm_b-base
odm_b-base, odm_b-cow, dm_b-src, odm_b的块设备路径分别如下:
odm_b-base: /dev/block/dm-47
odm_b-cow: /dev/block/dm-48
odm_b-src: /dev/block/dm-49
odm_b: /dev/block/dm-50
在FilesystemVerifierAction中校验odm的参数如下:
update_engine: Partition: odm
update_engine: source_size: 0
update_engine: source_path:
update_engine: source_hash:
update_engine: target_size: 244940800
update_engine: target_path:
update_engine: target_hash: 41DAEED1141E64AB01324FFAD46C5266A35EFCC256C24C0EA64E2EF3CF643202
update_engine: run_postinstall: false
update_engine: postinstall_path:
update_engine: readonly_target_path: /dev/block/mapper/odm_b
其中,readonly_target_path: /dev/block/mapper/odm_b的真实路径就是/dev/block/dm-50:
# ls -l /dev/block/mapper/
lrwxrwxrwx 1 root root 16 2025-01-21 09:10 odm_b -> /dev/block/dm-50
对/dev/block/mapper/odm_b做hash校验就是对/dev/block/dm-50做hash校验。 做hash校验时会产生读取块设备的I/O请求,然后被dm-user转发到snapuserd处理:
snapuserd: odm_b: Daemon: msg->seq: 0
snapuserd: odm_b: Daemon: msg->len: 131072
snapuserd: odm_b: Daemon: msg->sector: 0
snapuserd: odm_b: Daemon: msg->type: 0
snapuserd: odm_b: Daemon: msg->flags: 2048
snapuserd收到的操作请求: msg->type: 0是#define DM_USER_REQ_MAP_READ 0读的意思。
代码在 system/core/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_dm_user.cpp
bool Worker::RunThread() {
while (true) {
if (!ProcessIORequest()) {
break;
}
}
}
bool Worker::ProcessIORequest() {
struct dm_user_header* header = bufsink_.GetHeaderPtr();
switch (header->type) {
case DM_USER_REQ_MAP_READ: {
if (!DmuserReadRequest()) {
return false;
}
break;
}
}
}
bool Worker::DmuserReadRequest() {
return ReadAlignedSector(header->sector, header->len, true);
}
bool Worker::ReadAlignedSector() {
if (not_found) {
// Block not found in map - which means this block was not
// changed as per the OTA. Just route the I/O to the base
// device.
// 直接从Base设备读取
if (!ReadDataFromBaseDevice(sector, size)) {
} else {
// We found the sector in mapping. Check the type of COW OP and
// process it.
// 从COW OP获取
if (!ProcessCowOp(it->second)) {
SNAP_LOG(ERROR) << "ProcessCowOp failed";
header->type = DM_USER_RESP_ERROR;
}
}
}
快照合并
升级完成后, 新系统的每个动态分区都由两部分组成——老系统的分区(-base)和新旧系统的差分内容(-cow)。系统在升级完成后重启时需要将分区的原始内容(-base)与差分内容(-cow)进行动态的合并,并使用这合并后的内容启动系统 (此时仅仅只是动态的合并并加载到内存中,并没有物理上的合并)。
如果开机失败,那很简单,分区切回去,再次开机时不加载差分内容(-cow),也就是使用老系统开机。
如果开机成功,会在后台默默将差分内容(-cow)物理的合并进老系统的分区(-base)中,形成真正的新系统。