Construct the super_vbmeta image

This commit constructs the super_vbmeta image to eliminate the
need of adding /vbmeta_system and/or /vbmeta_vendor when AVB
chain partition is used with Android Dynamic Partition.

See BOARD_AVB_VBMETA_SYSTEM under the link:
https://android.googlesource.com/platform/external/avb/#build-system-integration

The structure of super_vbmeta :

|     VBMeta Table    | (fixed-length 2KiB)
| Backup VBMeta Table | (fixed-length 2KiB)
|     VBMeta Images   | (fixed-length 64KiB each)

The structure of VBMeta Table :

| Super VBMeta Header | (fixed-length 128B)
|  VBMeta Descriptors | (variable-length)

The VBMeta Table records the slot number of each
vbmeta image within the /super_vbmeta partition.

Bug: 137054296
Test: m libvbmeta_test
Test: ./out/host/linux-x86/nativetest/libvbmeta_test/libvbmeta_test
Change-Id: I01aeadd850750ae87d9125484c1b1f570bb84756
This commit is contained in:
Kaiwen Szu 2019-07-31 14:43:04 +08:00
parent 17438477cb
commit 6dd098cb1e
14 changed files with 1130 additions and 0 deletions

View file

@ -0,0 +1,52 @@
//
// Copyright (C) 2019 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.
//
libvbmeta_lib_deps = [
"libbase",
"libcrypto",
]
cc_library {
name: "libvbmeta",
host_supported: true,
srcs: [
"builder.cpp",
"reader.cpp",
"utility.cpp",
"writer.cpp",
],
shared_libs: [
"liblog",
] + libvbmeta_lib_deps,
export_include_dirs: ["include"],
}
cc_test_host {
name: "libvbmeta_test",
static_libs: [
"libsparse",
"libvbmeta",
"libz",
] + libvbmeta_lib_deps,
srcs: [
"builder_test.cpp",
"super_vbmeta_test.cpp",
],
required: [
"avbtool",
"vbmake",
],
}

View file

@ -0,0 +1,214 @@
/*
* Copyright (C) 2019 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 "builder.h"
#include <android-base/file.h>
#include <openssl/sha.h>
#include "reader.h"
#include "utility.h"
#include "writer.h"
using android::base::ErrnoError;
using android::base::Error;
using android::base::Result;
using android::base::unique_fd;
namespace android {
namespace fs_mgr {
SuperVBMetaBuilder::SuperVBMetaBuilder() {}
SuperVBMetaBuilder::SuperVBMetaBuilder(const int super_vbmeta_fd,
const std::map<std::string, std::string>& images_path)
: super_vbmeta_fd_(super_vbmeta_fd), images_path_(images_path) {}
Result<void> SuperVBMetaBuilder::Build() {
for (const auto& [vbmeta_name, file_path] : images_path_) {
Result<std::string> content = ReadVBMetaImageFromFile(file_path);
if (!content) {
return content.error();
}
Result<uint8_t> vbmeta_index = AddVBMetaImage(vbmeta_name);
if (!vbmeta_index) {
return vbmeta_index.error();
}
Result<void> rv_export_vbmeta_image =
ExportVBMetaImageToFile(vbmeta_index.value(), content.value());
if (!rv_export_vbmeta_image) {
return rv_export_vbmeta_image;
}
}
return {};
}
Result<std::string> SuperVBMetaBuilder::ReadVBMetaImageFromFile(const std::string& file) {
unique_fd source_fd(TEMP_FAILURE_RETRY(open(file.c_str(), O_RDONLY | O_CLOEXEC)));
if (source_fd < 0) {
return ErrnoError() << "Couldn't open vbmeta image file " << file;
}
Result<uint64_t> file_size = GetFileSize(source_fd);
if (!file_size) {
return file_size.error();
}
if (file_size.value() > VBMETA_IMAGE_MAX_SIZE) {
return Error() << "vbmeta image file size " << file_size.value() << " is too large";
}
std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(VBMETA_IMAGE_MAX_SIZE);
if (!android::base::ReadFully(source_fd, buffer.get(), file_size.value())) {
return ErrnoError() << "Couldn't read vbmeta image file " << file;
}
return std::string(reinterpret_cast<const char*>(buffer.get()), VBMETA_IMAGE_MAX_SIZE);
}
Result<uint8_t> SuperVBMetaBuilder::GetEmptySlot() {
for (uint8_t i = 0; i < VBMETA_IMAGE_MAX_NUM; ++i) {
if ((table_.header.in_use & (1 << i)) == 0) return i;
}
return Error() << "There isn't empty slot in super vbmeta";
}
Result<uint8_t> SuperVBMetaBuilder::AddVBMetaImage(const std::string& vbmeta_name) {
auto desc = std::find_if(
table_.descriptors.begin(), table_.descriptors.end(),
[&vbmeta_name](const auto& entry) { return entry.vbmeta_name == vbmeta_name; });
uint8_t slot_number = 0;
if (desc != table_.descriptors.end()) {
slot_number = desc->vbmeta_index;
} else {
Result<uint8_t> new_slot = GetEmptySlot();
if (!new_slot) {
return new_slot;
}
slot_number = new_slot.value();
// insert new descriptor into table
InternalVBMetaDescriptor new_desc;
new_desc.vbmeta_index = slot_number;
new_desc.vbmeta_name_length = vbmeta_name.length();
new_desc.vbmeta_name = vbmeta_name;
memset(new_desc.reserved, 0, sizeof(new_desc.reserved));
table_.descriptors.emplace_back(std::move(new_desc));
// mark slot as in use
table_.header.in_use |= (1 << slot_number);
}
return slot_number;
}
void SuperVBMetaBuilder::DeleteVBMetaImage(const std::string& vbmeta_name) {
auto desc = std::find_if(
table_.descriptors.begin(), table_.descriptors.end(),
[&vbmeta_name](const auto& entry) { return entry.vbmeta_name == vbmeta_name; });
if (desc != table_.descriptors.end()) {
// mark slot as not in use
table_.header.in_use &= ~(1 << desc->vbmeta_index);
// erase descriptor in table
table_.descriptors.erase(desc);
}
}
std::unique_ptr<VBMetaTable> SuperVBMetaBuilder::ExportVBMetaTable() {
// calculate descriptors size
uint32_t descriptors_size = 0;
for (const auto& desc : table_.descriptors) {
descriptors_size += SUPER_VBMETA_DESCRIPTOR_SIZE + desc.vbmeta_name_length * sizeof(char);
}
// export header
table_.header.magic = SUPER_VBMETA_MAGIC;
table_.header.major_version = SUPER_VBMETA_MAJOR_VERSION;
table_.header.minor_version = SUPER_VBMETA_MINOR_VERSION;
table_.header.header_size = SUPER_VBMETA_HEADER_SIZE;
table_.header.total_size = SUPER_VBMETA_HEADER_SIZE + descriptors_size;
memset(table_.header.checksum, 0, sizeof(table_.header.checksum));
table_.header.descriptors_size = descriptors_size;
memset(table_.header.reserved, 0, sizeof(table_.header.reserved));
std::string serialized_table = SerializeVBMetaTable(table_);
::SHA256(reinterpret_cast<const uint8_t*>(serialized_table.c_str()), table_.header.total_size,
&table_.header.checksum[0]);
return std::make_unique<VBMetaTable>(table_);
}
Result<void> SuperVBMetaBuilder::ExportVBMetaTableToFile() {
std::unique_ptr<VBMetaTable> table = ExportVBMetaTable();
std::string serialized_table = SerializeVBMetaTable(*table);
android::base::Result<void> rv_write_primary_vbmeta_table =
WritePrimaryVBMetaTable(super_vbmeta_fd_, serialized_table);
if (!rv_write_primary_vbmeta_table) {
return rv_write_primary_vbmeta_table;
}
android::base::Result<void> rv_write_backup_vbmeta_table =
WriteBackupVBMetaTable(super_vbmeta_fd_, serialized_table);
return rv_write_backup_vbmeta_table;
}
Result<void> SuperVBMetaBuilder::ExportVBMetaImageToFile(const uint8_t vbmeta_index,
const std::string& vbmeta_image) {
Result<void> rv_write_vbmeta_image =
WriteVBMetaImage(super_vbmeta_fd_, vbmeta_index, vbmeta_image);
if (!rv_write_vbmeta_image) {
return rv_write_vbmeta_image;
}
Result<void> rv_validate_vbmeta_image =
ValidateVBMetaImage(super_vbmeta_fd_, vbmeta_index, vbmeta_image);
return rv_validate_vbmeta_image;
}
bool WriteToSuperVBMetaFile(const std::string& super_vbmeta_file,
const std::map<std::string, std::string>& images_path) {
unique_fd super_vbmeta_fd(TEMP_FAILURE_RETRY(
open(super_vbmeta_file.c_str(), O_CREAT | O_RDWR | O_TRUNC | O_CLOEXEC, 0644)));
if (super_vbmeta_fd < 0) {
PERROR << "Couldn't open super vbmeta file " << super_vbmeta_file;
return false;
}
SuperVBMetaBuilder builder(super_vbmeta_fd, images_path);
Result<void> rv_build = builder.Build();
if (!rv_build) {
LERROR << rv_build.error();
return false;
}
Result<void> rv_export = builder.ExportVBMetaTableToFile();
if (!rv_export) {
LERROR << rv_export.error();
return false;
}
return true;
}
} // namespace fs_mgr
} // namespace android

View file

@ -0,0 +1,55 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <map>
#include <string>
#include <android-base/result.h>
#include "super_vbmeta_format.h"
namespace android {
namespace fs_mgr {
class SuperVBMetaBuilder {
public:
SuperVBMetaBuilder();
SuperVBMetaBuilder(const int super_vbmeta_fd,
const std::map<std::string, std::string>& images_path);
android::base::Result<void> Build();
android::base::Result<std::string> ReadVBMetaImageFromFile(const std::string& file);
// It adds the vbmeta image in super_vbmeta and returns the index
// (which has the same meaning with vbmeta_index of VBMetaDescriptor).
android::base::Result<uint8_t> AddVBMetaImage(const std::string& vbmeta_name);
void DeleteVBMetaImage(const std::string& vbmeta_name);
std::unique_ptr<VBMetaTable> ExportVBMetaTable();
android::base::Result<void> ExportVBMetaTableToFile();
android::base::Result<void> ExportVBMetaImageToFile(const uint8_t vbmeta_index,
const std::string& vbmeta_image);
private:
android::base::Result<uint8_t> GetEmptySlot();
int super_vbmeta_fd_;
VBMetaTable table_;
// Maps vbmeta image name to vbmeta image file path.
std::map<std::string, std::string> images_path_;
};
} // namespace fs_mgr
} // namespace android

View file

@ -0,0 +1,80 @@
/*
* Copyright (C) 2019 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 <gtest/gtest.h>
#include "builder.h"
#include "super_vbmeta_format.h"
using android::base::Result;
using android::fs_mgr::SuperVBMetaBuilder;
TEST(BuilderTest, VBMetaTableBasic) {
std::unique_ptr<SuperVBMetaBuilder> builder = std::make_unique<SuperVBMetaBuilder>();
ASSERT_NE(builder, nullptr);
Result<uint8_t> vbmeta_index = builder->AddVBMetaImage("vbmeta" /* vbmeta_name */
);
EXPECT_TRUE(vbmeta_index);
Result<uint8_t> vbmeta_system_slot = builder->AddVBMetaImage("vbmeta_system" /* vbmeta_name */
);
EXPECT_TRUE(vbmeta_system_slot);
Result<uint8_t> vbmeta_vendor_slot = builder->AddVBMetaImage("vbmeta_vendor" /* vbmeta_name */
);
EXPECT_TRUE(vbmeta_vendor_slot);
builder->DeleteVBMetaImage("vbmeta_system" /* vbmeta_name */
);
Result<uint8_t> vbmeta_product_slot = builder->AddVBMetaImage("vbmeta_product" /* vbmeta_name */
);
EXPECT_TRUE(vbmeta_product_slot);
std::unique_ptr<VBMetaTable> table = builder->ExportVBMetaTable();
ASSERT_NE(table, nullptr);
// check for vbmeta table header
EXPECT_EQ(table->header.magic, SUPER_VBMETA_MAGIC);
EXPECT_EQ(table->header.major_version, SUPER_VBMETA_MAJOR_VERSION);
EXPECT_EQ(table->header.minor_version, SUPER_VBMETA_MINOR_VERSION);
EXPECT_EQ(table->header.header_size, SUPER_VBMETA_HEADER_SIZE);
EXPECT_EQ(table->header.total_size,
SUPER_VBMETA_HEADER_SIZE + SUPER_VBMETA_DESCRIPTOR_SIZE * 3 + 33);
EXPECT_EQ(table->header.descriptors_size, SUPER_VBMETA_DESCRIPTOR_SIZE * 3 + 33);
// Test for vbmeta table descriptors
EXPECT_EQ(table->descriptors.size(), 3);
EXPECT_EQ(table->descriptors[0].vbmeta_index, 0);
EXPECT_EQ(table->descriptors[0].vbmeta_name_length, 6);
for (int i = 0; i < sizeof(table->descriptors[0].reserved); i++)
EXPECT_EQ(table->descriptors[0].reserved[i], 0);
EXPECT_EQ(table->descriptors[0].vbmeta_name, "vbmeta");
EXPECT_EQ(table->descriptors[1].vbmeta_index, 2);
EXPECT_EQ(table->descriptors[1].vbmeta_name_length, 13);
for (int i = 0; i < sizeof(table->descriptors[1].reserved); i++)
EXPECT_EQ(table->descriptors[1].reserved[i], 0);
EXPECT_EQ(table->descriptors[1].vbmeta_name, "vbmeta_vendor");
EXPECT_EQ(table->descriptors[2].vbmeta_index, 1);
EXPECT_EQ(table->descriptors[2].vbmeta_name_length, 14);
for (int i = 0; i < sizeof(table->descriptors[2].reserved); i++)
EXPECT_EQ(table->descriptors[2].reserved[i], 0);
EXPECT_EQ(table->descriptors[2].vbmeta_name, "vbmeta_product");
}

View file

@ -0,0 +1,29 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <map>
#include <string>
namespace android {
namespace fs_mgr {
bool WriteToSuperVBMetaFile(const std::string& super_vbmeta_file,
const std::map<std::string, std::string>& images_path);
} // namespace fs_mgr
} // namespace android

118
fs_mgr/libvbmeta/reader.cpp Normal file
View file

@ -0,0 +1,118 @@
/*
* Copyright (C) 2019 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 <android-base/file.h>
using android::base::ErrnoError;
using android::base::Error;
using android::base::Result;
namespace android {
namespace fs_mgr {
Result<void> LoadAndVerifySuperVBMetaHeader(const void* buffer, SuperVBMetaHeader* header) {
memcpy(header, buffer, sizeof(*header));
// Do basic validation of super vbmeta.
if (header->magic != SUPER_VBMETA_MAGIC) {
return Error() << "Super VBMeta has invalid magic value";
}
// Check that the version is compatible.
if (header->major_version != SUPER_VBMETA_MAJOR_VERSION ||
header->minor_version > SUPER_VBMETA_MINOR_VERSION) {
return Error() << "Super VBMeta has incompatible version";
}
return {};
}
void LoadVBMetaDescriptors(const void* buffer, uint32_t size,
std::vector<InternalVBMetaDescriptor>* descriptors) {
for (int p = 0; p < size;) {
InternalVBMetaDescriptor descriptor;
memcpy(&descriptor, (char*)buffer + p, SUPER_VBMETA_DESCRIPTOR_SIZE);
p += SUPER_VBMETA_DESCRIPTOR_SIZE;
descriptor.vbmeta_name = std::string((char*)buffer + p, descriptor.vbmeta_name_length);
p += descriptor.vbmeta_name_length;
descriptors->emplace_back(std::move(descriptor));
}
}
Result<void> ReadVBMetaTable(int fd, uint64_t offset, VBMetaTable* table) {
std::unique_ptr<uint8_t[]> header_buffer =
std::make_unique<uint8_t[]>(SUPER_VBMETA_HEADER_SIZE);
if (!android::base::ReadFullyAtOffset(fd, header_buffer.get(), SUPER_VBMETA_HEADER_SIZE,
offset)) {
return ErrnoError() << "Couldn't read super vbmeta header at offset " << offset;
}
Result<void> rv_header = LoadAndVerifySuperVBMetaHeader(header_buffer.get(), &table->header);
if (!rv_header) {
return rv_header;
}
const uint64_t descriptors_offset = offset + table->header.header_size;
std::unique_ptr<uint8_t[]> descriptors_buffer =
std::make_unique<uint8_t[]>(table->header.descriptors_size);
if (!android::base::ReadFullyAtOffset(fd, descriptors_buffer.get(),
table->header.descriptors_size, descriptors_offset)) {
return ErrnoError() << "Couldn't read super vbmeta descriptors at offset "
<< descriptors_offset;
}
LoadVBMetaDescriptors(descriptors_buffer.get(), table->header.descriptors_size,
&table->descriptors);
return {};
}
Result<void> ReadPrimaryVBMetaTable(int fd, VBMetaTable* table) {
uint64_t offset = PRIMARY_SUPER_VBMETA_TABLE_OFFSET;
return ReadVBMetaTable(fd, offset, table);
}
Result<void> ReadBackupVBMetaTable(int fd, VBMetaTable* table) {
uint64_t offset = BACKUP_SUPER_VBMETA_TABLE_OFFSET;
return ReadVBMetaTable(fd, offset, table);
}
Result<std::string> ReadVBMetaImage(int fd, int slot) {
const uint64_t offset = 2 * SUPER_VBMETA_TABLE_MAX_SIZE + slot * VBMETA_IMAGE_MAX_SIZE;
std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(VBMETA_IMAGE_MAX_SIZE);
if (!android::base::ReadFullyAtOffset(fd, buffer.get(), VBMETA_IMAGE_MAX_SIZE, offset)) {
return ErrnoError() << "Couldn't read vbmeta image at offset " << offset;
}
return std::string(reinterpret_cast<char*>(buffer.get()), VBMETA_IMAGE_MAX_SIZE);
}
Result<void> ValidateVBMetaImage(int super_vbmeta_fd, int vbmeta_index,
const std::string& vbmeta_image) {
Result<std::string> content = ReadVBMetaImage(super_vbmeta_fd, vbmeta_index);
if (!content) {
return content.error();
}
if (vbmeta_image != content.value()) {
return Error() << "VBMeta Image in Super VBMeta differ from the original one.";
}
return {};
}
} // namespace fs_mgr
} // namespace android

34
fs_mgr/libvbmeta/reader.h Normal file
View file

@ -0,0 +1,34 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <android-base/result.h>
#include "super_vbmeta_format.h"
namespace android {
namespace fs_mgr {
android::base::Result<void> ReadPrimaryVBMetaTable(int fd, VBMetaTable* table);
android::base::Result<void> ReadBackupVBMetaTable(int fd, VBMetaTable* table);
android::base::Result<std::string> ReadVBMetaImage(int fd, int slot);
android::base::Result<void> ValidateVBMetaImage(int super_vbmeta_fd, int vbmeta_index,
const std::string& vbmeta_image);
} // namespace fs_mgr
} // namespace android

View file

@ -0,0 +1,34 @@
/*
* Copyright (C) 2019 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.
*/
/* This .h file is intended for CPP clients (usually fastbootd, recovery and update_engine) */
#pragma once
#include <string>
#include <vector>
#include "super_vbmeta_format_c.h"
struct InternalVBMetaDescriptor : VBMetaDescriptor {
/* 64: The vbmeta image's name */
std::string vbmeta_name;
};
struct VBMetaTable {
SuperVBMetaHeader header;
std::vector<InternalVBMetaDescriptor> descriptors;
};

View file

@ -0,0 +1,122 @@
/*
* Copyright (C) 2019 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.
*/
/* This .h file is intended for C clients (usually bootloader). */
#pragma once
#include <stdint.h>
/* Magic signature for super vbmeta. */
#define SUPER_VBMETA_MAGIC 0x5356424d
/* Current super vbmeta version. */
#define SUPER_VBMETA_MAJOR_VERSION 1
#define SUPER_VBMETA_MINOR_VERSION 0
/* super vbmeta size. */
#define SUPER_VBMETA_HEADER_SIZE sizeof(SuperVBMetaHeader)
#define SUPER_VBMETA_DESCRIPTOR_SIZE sizeof(VBMetaDescriptor)
#define SUPER_VBMETA_TABLE_MAX_SIZE 2048
/* super vbmeta offset. */
#define PRIMARY_SUPER_VBMETA_TABLE_OFFSET 0
#define BACKUP_SUPER_VBMETA_TABLE_OFFSET SUPER_VBMETA_TABLE_MAX_SIZE
/* restriction of vbmeta image */
#define VBMETA_IMAGE_MAX_NUM 32
#define VBMETA_IMAGE_MAX_SIZE 64 * 1024
/* Binary format of the super vbmeta image.
*
* The super vbmeta image consists of two blocks:
*
* +------------------------------------------+
* | Super VBMeta Table - fixed size |
* +------------------------------------------+
* | Backup Super VBMeta Table - fixed size |
* +------------------------------------------+
* | VBMeta Images - fixed size |
* +------------------------------------------+
*
* The "Super VBMeta Table" records the offset
* and the size of each vbmeta_partition within
* /super_vbmeta.
*
* The "VBMeta Image" is copied from each vbmeta_partition
* and filled with 0 until 64K bytes.
*
* The super vbmeta table consists of two blocks:
*
* +-----------------------------------------+
* | Header data - fixed size |
* +-----------------------------------------+
* | VBMeta descriptors - variable size |
* +-----------------------------------------+
*
* The "Header data" block is described by the following struct and
* is always 128 bytes long.
*
* The "VBMeta descriptor" is |descriptors_size| + |vbmeta_name_length|
* bytes long. It contains the offset and size for each vbmeta image
* and is followed by |vbmeta_name_length| bytes of the partition name
* (UTF-8 encoded).
*/
typedef struct SuperVBMetaHeader {
/* 0: Magic signature (SUPER_VBMETA_MAGIC). */
uint32_t magic;
/* 4: Major version. Version number required to read this super vbmeta. If the version is not
* equal to the library version, the super vbmeta should be considered incompatible.
*/
uint16_t major_version;
/* 6: Minor version. A library supporting newer features should be able to
* read super vbmeta with an older minor version. However, an older library
* should not support reading super vbmeta if its minor version is higher.
*/
uint16_t minor_version;
/* 8: The size of this header struct. */
uint32_t header_size;
/* 12: The size of this super vbmeta table. */
uint32_t total_size;
/* 16: SHA256 checksum of this super vbmeta table, with this field set to 0. */
uint8_t checksum[32];
/* 48: The size of the vbmeta table descriptors. */
uint32_t descriptors_size;
/* 52: mark which slot is in use. */
uint32_t in_use = 0;
/* 56: reserved for other usage, filled with 0. */
uint8_t reserved[72];
} __attribute__((packed)) SuperVBMetaHeader;
typedef struct VBMetaDescriptor {
/* 0: The slot number of the vbmeta image. */
uint8_t vbmeta_index;
/* 12: The length of the vbmeta image name. */
uint32_t vbmeta_name_length;
/* 16: Space reserved for other usage, filled with 0. */
uint8_t reserved[48];
} __attribute__((packed)) VBMetaDescriptor;

View file

@ -0,0 +1,191 @@
/*
* Copyright (C) 2019 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 <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <android-base/file.h>
#include <gtest/gtest.h>
#include <openssl/sha.h>
#include <sparse/sparse.h>
#include "reader.h"
#include "super_vbmeta_format.h"
#include "utility.h"
#include "writer.h"
#define FAKE_DATA_SIZE 40960
#define FAKE_PARTITION_SIZE FAKE_DATA_SIZE * 25
using android::base::Result;
using android::fs_mgr::GetFileSize;
using android::fs_mgr::ReadVBMetaImage;
using SparsePtr = std::unique_ptr<sparse_file, decltype(&sparse_file_destroy)>;
void GeneratePartitionImage(int fd, const std::string& file_name,
const std::string& partition_name) {
std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(FAKE_DATA_SIZE);
for (size_t c = 0; c < FAKE_DATA_SIZE; c++) {
buffer[c] = uint8_t(c);
}
SparsePtr file(sparse_file_new(512 /* block size */, FAKE_DATA_SIZE), sparse_file_destroy);
EXPECT_TRUE(file);
EXPECT_EQ(0, sparse_file_add_data(file.get(), buffer.get(), FAKE_DATA_SIZE,
0 /* offset in blocks */));
EXPECT_EQ(0, sparse_file_write(file.get(), fd, false /* gz */, true /* sparse */,
false /* crc */));
std::stringstream cmd;
cmd << "avbtool add_hashtree_footer"
<< " --image " << file_name << " --partition_name " << partition_name
<< " --partition_size " << FAKE_PARTITION_SIZE << " --algorithm SHA256_RSA2048"
<< " --key external/avb/test/data/testkey_rsa2048.pem";
int rc = system(cmd.str().c_str());
EXPECT_TRUE(WIFEXITED(rc));
EXPECT_EQ(WEXITSTATUS(rc), 0);
}
void GenerateVBMetaImage(const std::string& vbmeta_file_name,
const std::string& include_file_name) {
std::stringstream cmd;
cmd << "avbtool make_vbmeta_image"
<< " --output " << vbmeta_file_name << " --include_descriptors_from_image "
<< include_file_name;
int rc = system(cmd.str().c_str());
EXPECT_TRUE(WIFEXITED(rc));
EXPECT_EQ(WEXITSTATUS(rc), 0);
}
std::string ReadVBMetaImageFromFile(const std::string& file) {
android::base::unique_fd fd(open(file.c_str(), O_RDONLY | O_CLOEXEC));
EXPECT_GT(fd, 0);
Result<uint64_t> file_size = GetFileSize(fd);
EXPECT_TRUE(file_size);
std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(VBMETA_IMAGE_MAX_SIZE);
EXPECT_TRUE(android::base::ReadFully(fd, buffer.get(), file_size.value()));
return std::string(reinterpret_cast<char*>(buffer.get()), VBMETA_IMAGE_MAX_SIZE);
}
TEST(VBMetaTableTest, VBMetaTableBasic) {
TemporaryDir td;
// Generate Partition Image
TemporaryFile system_tf(std::string(td.path));
std::string system_path(system_tf.path);
GeneratePartitionImage(system_tf.fd, system_path, "system");
system_tf.release();
TemporaryFile vendor_tf(std::string(td.path));
std::string vendor_path(vendor_tf.path);
GeneratePartitionImage(vendor_tf.fd, vendor_path, "vendor");
vendor_tf.release();
TemporaryFile product_tf(std::string(td.path));
std::string product_path(product_tf.path);
GeneratePartitionImage(product_tf.fd, product_path, "product");
product_tf.release();
// Generate VBMeta Image
std::string vbmeta_system_path(td.path);
vbmeta_system_path.append("/vbmeta_system.img");
GenerateVBMetaImage(vbmeta_system_path, system_path);
std::string vbmeta_vendor_path(td.path);
vbmeta_vendor_path.append("/vbmeta_vendor.img");
GenerateVBMetaImage(vbmeta_vendor_path, vendor_path);
std::string vbmeta_product_path(td.path);
vbmeta_product_path.append("/vbmeta_product.img");
GenerateVBMetaImage(vbmeta_product_path, product_path);
// Generate Super VBMeta Image
std::string super_vbmeta_path(td.path);
super_vbmeta_path.append("/super_vbmeta.img");
std::stringstream cmd;
cmd << "vbmake"
<< " --image "
<< "vbmeta_system"
<< "=" << vbmeta_system_path << " --image "
<< "vbmeta_vendor"
<< "=" << vbmeta_vendor_path << " --image "
<< "vbmeta_product"
<< "=" << vbmeta_product_path << " --output=" << super_vbmeta_path;
int rc = system(cmd.str().c_str());
ASSERT_TRUE(WIFEXITED(rc));
ASSERT_EQ(WEXITSTATUS(rc), 0);
android::base::unique_fd fd(open(super_vbmeta_path.c_str(), O_RDONLY | O_CLOEXEC));
EXPECT_GT(fd, 0);
// Check the size of vbmeta table
Result<uint64_t> super_vbmeta_size = GetFileSize(fd);
EXPECT_TRUE(super_vbmeta_size);
EXPECT_EQ(super_vbmeta_size.value(),
SUPER_VBMETA_TABLE_MAX_SIZE * 2 + VBMETA_IMAGE_MAX_SIZE * 3);
// Check Primary vbmeta table is equal to Backup one
VBMetaTable table;
EXPECT_TRUE(android::fs_mgr::ReadPrimaryVBMetaTable(fd, &table));
VBMetaTable table_backup;
EXPECT_TRUE(android::fs_mgr::ReadBackupVBMetaTable(fd, &table_backup));
EXPECT_EQ(android::fs_mgr::SerializeVBMetaTable(table),
android::fs_mgr::SerializeVBMetaTable(table_backup));
// Check vbmeta table Header Checksum
std::string serial_table = android::fs_mgr::SerializeVBMetaTable(table);
std::string serial_removed_checksum(serial_table);
// Replace checksum 32 bytes (starts at 16th byte) with 0
serial_removed_checksum.replace(16, 32, 32, 0);
uint8_t test_checksum[32];
::SHA256(reinterpret_cast<const uint8_t*>(serial_removed_checksum.c_str()),
table.header.total_size, &test_checksum[0]);
EXPECT_EQ(memcmp(table.header.checksum, test_checksum, 32), 0);
// Check vbmeta table descriptors and vbmeta images
EXPECT_EQ(table.descriptors.size(), 3);
EXPECT_EQ(table.descriptors[0].vbmeta_index, 0);
EXPECT_EQ(table.descriptors[0].vbmeta_name_length, 14);
EXPECT_EQ(table.descriptors[0].vbmeta_name, "vbmeta_product");
Result<std::string> vbmeta_product_content = ReadVBMetaImage(fd, 0);
EXPECT_TRUE(vbmeta_product_content);
EXPECT_EQ(ReadVBMetaImageFromFile(vbmeta_product_path), vbmeta_product_content.value());
EXPECT_EQ(table.descriptors[1].vbmeta_index, 1);
EXPECT_EQ(table.descriptors[1].vbmeta_name_length, 13);
EXPECT_EQ(table.descriptors[1].vbmeta_name, "vbmeta_system");
Result<std::string> vbmeta_system_content = ReadVBMetaImage(fd, 1);
EXPECT_TRUE(vbmeta_system_content);
EXPECT_EQ(ReadVBMetaImageFromFile(vbmeta_system_path), vbmeta_system_content.value());
EXPECT_EQ(table.descriptors[2].vbmeta_index, 2);
EXPECT_EQ(table.descriptors[2].vbmeta_name_length, 13);
EXPECT_EQ(table.descriptors[2].vbmeta_name, "vbmeta_vendor");
Result<std::string> vbmeta_vendor_content = ReadVBMetaImage(fd, 2);
EXPECT_TRUE(vbmeta_vendor_content);
EXPECT_EQ(ReadVBMetaImageFromFile(vbmeta_vendor_path), vbmeta_vendor_content.value());
}
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View file

@ -0,0 +1,47 @@
/*
* Copyright (C) 2019 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 "utility.h"
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "super_vbmeta_format.h"
using android::base::ErrnoError;
using android::base::Error;
using android::base::Result;
namespace android {
namespace fs_mgr {
Result<uint64_t> GetFileSize(int fd) {
struct stat sb;
if (fstat(fd, &sb) == -1) {
return ErrnoError() << "Couldn't get the file size";
}
return sb.st_size;
}
uint64_t IndexOffset(const uint8_t vbmeta_index) {
/* There are primary and backup vbmeta table in super_vbmeta,
so SUPER_VBMETA_TABLE_MAX_SIZE is counted twice. */
return 2 * SUPER_VBMETA_TABLE_MAX_SIZE + vbmeta_index * VBMETA_IMAGE_MAX_SIZE;
}
} // namespace fs_mgr
} // namespace android

View file

@ -0,0 +1,37 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <android-base/logging.h>
#include <android-base/result.h>
#define VBMETA_TAG "[libvbmeta]"
#define LWARN LOG(WARNING) << VBMETA_TAG
#define LINFO LOG(INFO) << VBMETA_TAG
#define LERROR LOG(ERROR) << VBMETA_TAG
#define PWARNING PLOG(WARNING) << VBMETA_TAG
#define PERROR PLOG(ERROR) << VBMETA_TAG
namespace android {
namespace fs_mgr {
android::base::Result<uint64_t> GetFileSize(int fd);
uint64_t IndexOffset(const uint8_t vbmeta_index);
} // namespace fs_mgr
} // namespace android

View file

@ -0,0 +1,81 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "writer.h"
#include <android-base/file.h>
#include "utility.h"
using android::base::ErrnoError;
using android::base::Result;
namespace android {
namespace fs_mgr {
std::string SerializeVBMetaTable(const VBMetaTable& input) {
std::string table;
table.append(reinterpret_cast<const char*>(&input.header), SUPER_VBMETA_HEADER_SIZE);
for (const auto& desc : input.descriptors) {
table.append(reinterpret_cast<const char*>(&desc), SUPER_VBMETA_DESCRIPTOR_SIZE);
table.append(desc.vbmeta_name);
}
// Ensure the size of vbmeta table is SUPER_VBMETA_TABLE_MAX_SIZE
table.resize(SUPER_VBMETA_TABLE_MAX_SIZE, '\0');
return table;
}
Result<void> WritePrimaryVBMetaTable(int fd, const std::string& table) {
const uint64_t offset = PRIMARY_SUPER_VBMETA_TABLE_OFFSET;
if (lseek(fd, offset, SEEK_SET) < 0) {
return ErrnoError() << __PRETTY_FUNCTION__ << " lseek failed";
}
if (!android::base::WriteFully(fd, table.data(), table.size())) {
return ErrnoError() << "Failed to write primary vbmeta table at offset " << offset;
}
return {};
}
Result<void> WriteBackupVBMetaTable(int fd, const std::string& table) {
const uint64_t offset = BACKUP_SUPER_VBMETA_TABLE_OFFSET;
if (lseek(fd, offset, SEEK_SET) < 0) {
return ErrnoError() << __PRETTY_FUNCTION__ << " lseek failed";
}
if (!android::base::WriteFully(fd, table.data(), table.size())) {
return ErrnoError() << "Failed to write backup vbmeta table at offset " << offset;
}
return {};
}
Result<void> WriteVBMetaImage(int fd, const uint8_t slot_number, const std::string& vbmeta_image) {
const uint64_t offset = IndexOffset(slot_number);
if (lseek(fd, offset, SEEK_SET) < 0) {
return ErrnoError() << __PRETTY_FUNCTION__ << " lseek failed";
}
if (!android::base::WriteFully(fd, vbmeta_image.data(), vbmeta_image.size())) {
return ErrnoError() << "Failed to write vbmeta image at offset " << offset;
}
return {};
}
} // namespace fs_mgr
} // namespace android

36
fs_mgr/libvbmeta/writer.h Normal file
View file

@ -0,0 +1,36 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <string>
#include <android-base/result.h>
#include "super_vbmeta_format.h"
namespace android {
namespace fs_mgr {
std::string SerializeVBMetaTable(const VBMetaTable& input);
android::base::Result<void> WritePrimaryVBMetaTable(int fd, const std::string& table);
android::base::Result<void> WriteBackupVBMetaTable(int fd, const std::string& table);
android::base::Result<void> WriteVBMetaImage(int fd, const uint8_t slot_number,
const std::string& vbmeta_image);
} // namespace fs_mgr
} // namespace android