diff --git a/fs_mgr/libvbmeta/Android.bp b/fs_mgr/libvbmeta/Android.bp new file mode 100644 index 000000000..937e0f31f --- /dev/null +++ b/fs_mgr/libvbmeta/Android.bp @@ -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", + ], +} \ No newline at end of file diff --git a/fs_mgr/libvbmeta/builder.cpp b/fs_mgr/libvbmeta/builder.cpp new file mode 100644 index 000000000..a901a4f8e --- /dev/null +++ b/fs_mgr/libvbmeta/builder.cpp @@ -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 +#include + +#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& images_path) + : super_vbmeta_fd_(super_vbmeta_fd), images_path_(images_path) {} + +Result SuperVBMetaBuilder::Build() { + for (const auto& [vbmeta_name, file_path] : images_path_) { + Result content = ReadVBMetaImageFromFile(file_path); + if (!content) { + return content.error(); + } + + Result vbmeta_index = AddVBMetaImage(vbmeta_name); + if (!vbmeta_index) { + return vbmeta_index.error(); + } + + Result rv_export_vbmeta_image = + ExportVBMetaImageToFile(vbmeta_index.value(), content.value()); + if (!rv_export_vbmeta_image) { + return rv_export_vbmeta_image; + } + } + return {}; +} + +Result 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 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 buffer = std::make_unique(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(buffer.get()), VBMETA_IMAGE_MAX_SIZE); +} + +Result 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 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 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 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(serialized_table.c_str()), table_.header.total_size, + &table_.header.checksum[0]); + + return std::make_unique(table_); +} + +Result SuperVBMetaBuilder::ExportVBMetaTableToFile() { + std::unique_ptr table = ExportVBMetaTable(); + + std::string serialized_table = SerializeVBMetaTable(*table); + + android::base::Result 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 rv_write_backup_vbmeta_table = + WriteBackupVBMetaTable(super_vbmeta_fd_, serialized_table); + return rv_write_backup_vbmeta_table; +} + +Result SuperVBMetaBuilder::ExportVBMetaImageToFile(const uint8_t vbmeta_index, + const std::string& vbmeta_image) { + Result rv_write_vbmeta_image = + WriteVBMetaImage(super_vbmeta_fd_, vbmeta_index, vbmeta_image); + if (!rv_write_vbmeta_image) { + return rv_write_vbmeta_image; + } + + Result 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& 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 rv_build = builder.Build(); + if (!rv_build) { + LERROR << rv_build.error(); + return false; + } + + Result rv_export = builder.ExportVBMetaTableToFile(); + if (!rv_export) { + LERROR << rv_export.error(); + return false; + } + + return true; +} + +} // namespace fs_mgr +} // namespace android \ No newline at end of file diff --git a/fs_mgr/libvbmeta/builder.h b/fs_mgr/libvbmeta/builder.h new file mode 100644 index 000000000..58ea36a1c --- /dev/null +++ b/fs_mgr/libvbmeta/builder.h @@ -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 +#include + +#include + +#include "super_vbmeta_format.h" + +namespace android { +namespace fs_mgr { + +class SuperVBMetaBuilder { + public: + SuperVBMetaBuilder(); + SuperVBMetaBuilder(const int super_vbmeta_fd, + const std::map& images_path); + android::base::Result Build(); + android::base::Result 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 AddVBMetaImage(const std::string& vbmeta_name); + void DeleteVBMetaImage(const std::string& vbmeta_name); + std::unique_ptr ExportVBMetaTable(); + android::base::Result ExportVBMetaTableToFile(); + android::base::Result ExportVBMetaImageToFile(const uint8_t vbmeta_index, + const std::string& vbmeta_image); + + private: + android::base::Result GetEmptySlot(); + + int super_vbmeta_fd_; + VBMetaTable table_; + // Maps vbmeta image name to vbmeta image file path. + std::map images_path_; +}; + +} // namespace fs_mgr +} // namespace android \ No newline at end of file diff --git a/fs_mgr/libvbmeta/builder_test.cpp b/fs_mgr/libvbmeta/builder_test.cpp new file mode 100644 index 000000000..9a015fd36 --- /dev/null +++ b/fs_mgr/libvbmeta/builder_test.cpp @@ -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 + +#include "builder.h" +#include "super_vbmeta_format.h" + +using android::base::Result; +using android::fs_mgr::SuperVBMetaBuilder; + +TEST(BuilderTest, VBMetaTableBasic) { + std::unique_ptr builder = std::make_unique(); + ASSERT_NE(builder, nullptr); + + Result vbmeta_index = builder->AddVBMetaImage("vbmeta" /* vbmeta_name */ + ); + EXPECT_TRUE(vbmeta_index); + + Result vbmeta_system_slot = builder->AddVBMetaImage("vbmeta_system" /* vbmeta_name */ + ); + EXPECT_TRUE(vbmeta_system_slot); + + Result vbmeta_vendor_slot = builder->AddVBMetaImage("vbmeta_vendor" /* vbmeta_name */ + ); + EXPECT_TRUE(vbmeta_vendor_slot); + + builder->DeleteVBMetaImage("vbmeta_system" /* vbmeta_name */ + ); + + Result vbmeta_product_slot = builder->AddVBMetaImage("vbmeta_product" /* vbmeta_name */ + ); + EXPECT_TRUE(vbmeta_product_slot); + + std::unique_ptr 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"); +} \ No newline at end of file diff --git a/fs_mgr/libvbmeta/include/libvbmeta/libvbmeta.h b/fs_mgr/libvbmeta/include/libvbmeta/libvbmeta.h new file mode 100644 index 000000000..ab7ba737d --- /dev/null +++ b/fs_mgr/libvbmeta/include/libvbmeta/libvbmeta.h @@ -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 +#include + +namespace android { +namespace fs_mgr { + +bool WriteToSuperVBMetaFile(const std::string& super_vbmeta_file, + const std::map& images_path); + +} // namespace fs_mgr +} // namespace android \ No newline at end of file diff --git a/fs_mgr/libvbmeta/reader.cpp b/fs_mgr/libvbmeta/reader.cpp new file mode 100644 index 000000000..212d186ea --- /dev/null +++ b/fs_mgr/libvbmeta/reader.cpp @@ -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 + +using android::base::ErrnoError; +using android::base::Error; +using android::base::Result; + +namespace android { +namespace fs_mgr { + +Result 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* 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 ReadVBMetaTable(int fd, uint64_t offset, VBMetaTable* table) { + std::unique_ptr header_buffer = + std::make_unique(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 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 descriptors_buffer = + std::make_unique(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 ReadPrimaryVBMetaTable(int fd, VBMetaTable* table) { + uint64_t offset = PRIMARY_SUPER_VBMETA_TABLE_OFFSET; + return ReadVBMetaTable(fd, offset, table); +} + +Result ReadBackupVBMetaTable(int fd, VBMetaTable* table) { + uint64_t offset = BACKUP_SUPER_VBMETA_TABLE_OFFSET; + return ReadVBMetaTable(fd, offset, table); +} + +Result ReadVBMetaImage(int fd, int slot) { + const uint64_t offset = 2 * SUPER_VBMETA_TABLE_MAX_SIZE + slot * VBMETA_IMAGE_MAX_SIZE; + std::unique_ptr buffer = std::make_unique(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(buffer.get()), VBMETA_IMAGE_MAX_SIZE); +} + +Result ValidateVBMetaImage(int super_vbmeta_fd, int vbmeta_index, + const std::string& vbmeta_image) { + Result 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 diff --git a/fs_mgr/libvbmeta/reader.h b/fs_mgr/libvbmeta/reader.h new file mode 100644 index 000000000..f0997ff1f --- /dev/null +++ b/fs_mgr/libvbmeta/reader.h @@ -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 + +#include "super_vbmeta_format.h" + +namespace android { +namespace fs_mgr { + +android::base::Result ReadPrimaryVBMetaTable(int fd, VBMetaTable* table); +android::base::Result ReadBackupVBMetaTable(int fd, VBMetaTable* table); +android::base::Result ReadVBMetaImage(int fd, int slot); + +android::base::Result ValidateVBMetaImage(int super_vbmeta_fd, int vbmeta_index, + const std::string& vbmeta_image); + +} // namespace fs_mgr +} // namespace android \ No newline at end of file diff --git a/fs_mgr/libvbmeta/super_vbmeta_format.h b/fs_mgr/libvbmeta/super_vbmeta_format.h new file mode 100644 index 000000000..c62a2dd48 --- /dev/null +++ b/fs_mgr/libvbmeta/super_vbmeta_format.h @@ -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 +#include + +#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 descriptors; +}; \ No newline at end of file diff --git a/fs_mgr/libvbmeta/super_vbmeta_format_c.h b/fs_mgr/libvbmeta/super_vbmeta_format_c.h new file mode 100644 index 000000000..6d7980155 --- /dev/null +++ b/fs_mgr/libvbmeta/super_vbmeta_format_c.h @@ -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 + +/* 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; \ No newline at end of file diff --git a/fs_mgr/libvbmeta/super_vbmeta_test.cpp b/fs_mgr/libvbmeta/super_vbmeta_test.cpp new file mode 100644 index 000000000..6b4fc5d33 --- /dev/null +++ b/fs_mgr/libvbmeta/super_vbmeta_test.cpp @@ -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 +#include +#include + +#include +#include +#include +#include + +#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; + +void GeneratePartitionImage(int fd, const std::string& file_name, + const std::string& partition_name) { + std::unique_ptr buffer = std::make_unique(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 file_size = GetFileSize(fd); + EXPECT_TRUE(file_size); + std::unique_ptr buffer = std::make_unique(VBMETA_IMAGE_MAX_SIZE); + EXPECT_TRUE(android::base::ReadFully(fd, buffer.get(), file_size.value())); + return std::string(reinterpret_cast(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 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(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 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 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 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(); +} \ No newline at end of file diff --git a/fs_mgr/libvbmeta/utility.cpp b/fs_mgr/libvbmeta/utility.cpp new file mode 100644 index 000000000..c1842274f --- /dev/null +++ b/fs_mgr/libvbmeta/utility.cpp @@ -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 +#include +#include + +#include "super_vbmeta_format.h" + +using android::base::ErrnoError; +using android::base::Error; +using android::base::Result; + +namespace android { +namespace fs_mgr { + +Result 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 diff --git a/fs_mgr/libvbmeta/utility.h b/fs_mgr/libvbmeta/utility.h new file mode 100644 index 000000000..91db0ad45 --- /dev/null +++ b/fs_mgr/libvbmeta/utility.h @@ -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 +#include + +#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 GetFileSize(int fd); + +uint64_t IndexOffset(const uint8_t vbmeta_index); + +} // namespace fs_mgr +} // namespace android \ No newline at end of file diff --git a/fs_mgr/libvbmeta/writer.cpp b/fs_mgr/libvbmeta/writer.cpp new file mode 100644 index 000000000..fc0e0fb32 --- /dev/null +++ b/fs_mgr/libvbmeta/writer.cpp @@ -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 + +#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(&input.header), SUPER_VBMETA_HEADER_SIZE); + + for (const auto& desc : input.descriptors) { + table.append(reinterpret_cast(&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 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 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 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 \ No newline at end of file diff --git a/fs_mgr/libvbmeta/writer.h b/fs_mgr/libvbmeta/writer.h new file mode 100644 index 000000000..f8eed3684 --- /dev/null +++ b/fs_mgr/libvbmeta/writer.h @@ -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 + +#include + +#include "super_vbmeta_format.h" + +namespace android { +namespace fs_mgr { + +std::string SerializeVBMetaTable(const VBMetaTable& input); + +android::base::Result WritePrimaryVBMetaTable(int fd, const std::string& table); +android::base::Result WriteBackupVBMetaTable(int fd, const std::string& table); +android::base::Result WriteVBMetaImage(int fd, const uint8_t slot_number, + const std::string& vbmeta_image); + +} // namespace fs_mgr +} // namespace android \ No newline at end of file