You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1147 lines
39 KiB
1147 lines
39 KiB
/*
|
|
* 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 "gsi_service.h"
|
|
|
|
#include <sys/statvfs.h>
|
|
#include <sys/vfs.h>
|
|
#include <unistd.h>
|
|
|
|
#include <array>
|
|
#include <chrono>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include <android-base/errors.h>
|
|
#include <android-base/file.h>
|
|
#include <android-base/logging.h>
|
|
#include <android-base/properties.h>
|
|
#include <android-base/stringprintf.h>
|
|
#include <android-base/strings.h>
|
|
#include <android/gsi/BnImageService.h>
|
|
#include <android/gsi/IGsiService.h>
|
|
#include <android/os/IVold.h>
|
|
#include <binder/IServiceManager.h>
|
|
#include <binder/LazyServiceRegistrar.h>
|
|
#include <ext4_utils/ext4_utils.h>
|
|
#include <fs_mgr.h>
|
|
#include <libavb/libavb.h>
|
|
#include <libdm/dm.h>
|
|
#include <libfiemap/image_manager.h>
|
|
#include <openssl/sha.h>
|
|
#include <private/android_filesystem_config.h>
|
|
#include <selinux/android.h>
|
|
#include <storage_literals/storage_literals.h>
|
|
|
|
#include "file_paths.h"
|
|
#include "libgsi_private.h"
|
|
|
|
namespace android {
|
|
namespace gsi {
|
|
|
|
using namespace std::literals;
|
|
using namespace android::fs_mgr;
|
|
using namespace android::fiemap;
|
|
using namespace android::storage_literals;
|
|
using android::base::ReadFileToString;
|
|
using android::base::ReadFullyAtOffset;
|
|
using android::base::RemoveFileIfExists;
|
|
using android::base::SetProperty;
|
|
using android::base::StringPrintf;
|
|
using android::base::unique_fd;
|
|
using android::base::WriteStringToFd;
|
|
using android::base::WriteStringToFile;
|
|
using android::binder::LazyServiceRegistrar;
|
|
using android::dm::DeviceMapper;
|
|
|
|
// Default userdata image size.
|
|
static constexpr int64_t kDefaultUserdataSize = int64_t(2) * 1024 * 1024 * 1024;
|
|
|
|
static bool GetAvbPublicKeyFromFd(int fd, AvbPublicKey* dst);
|
|
|
|
// Fix the file contexts of dsu metadata files.
|
|
// By default, newly created files inherit the file contexts of their parent
|
|
// directory. Since globally readable public metadata files are labeled with a
|
|
// different context, gsi_public_metadata_file, we need to call this function to
|
|
// fix their contexts after creating them.
|
|
static void RestoreconMetadataFiles() {
|
|
auto flags = SELINUX_ANDROID_RESTORECON_RECURSE | SELINUX_ANDROID_RESTORECON_SKIP_SEHASH;
|
|
selinux_android_restorecon(DSU_METADATA_PREFIX, flags);
|
|
}
|
|
|
|
GsiService::GsiService() {
|
|
progress_ = {};
|
|
}
|
|
|
|
void GsiService::Register() {
|
|
auto lazyRegistrar = LazyServiceRegistrar::getInstance();
|
|
android::sp<GsiService> service = new GsiService();
|
|
auto ret = lazyRegistrar.registerService(service, kGsiServiceName);
|
|
|
|
if (ret != android::OK) {
|
|
LOG(FATAL) << "Could not register gsi service: " << ret;
|
|
}
|
|
}
|
|
|
|
#define ENFORCE_SYSTEM \
|
|
do { \
|
|
binder::Status status = CheckUid(); \
|
|
if (!status.isOk()) return status; \
|
|
} while (0)
|
|
|
|
#define ENFORCE_SYSTEM_OR_SHELL \
|
|
do { \
|
|
binder::Status status = CheckUid(AccessLevel::SystemOrShell); \
|
|
if (!status.isOk()) return status; \
|
|
} while (0)
|
|
|
|
int GsiService::SaveInstallation(const std::string& installation) {
|
|
auto dsu_slot = GetDsuSlot(installation);
|
|
auto install_dir_file = DsuInstallDirFile(dsu_slot);
|
|
auto metadata_dir = android::base::Dirname(install_dir_file);
|
|
if (access(metadata_dir.c_str(), F_OK) != 0) {
|
|
if (mkdir(metadata_dir.c_str(), 0777) != 0) {
|
|
PLOG(ERROR) << "Failed to mkdir " << metadata_dir;
|
|
return INSTALL_ERROR_GENERIC;
|
|
}
|
|
}
|
|
auto fd = android::base::unique_fd(
|
|
open(install_dir_file.c_str(), O_RDWR | O_SYNC | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR));
|
|
if (!WriteStringToFd(installation, fd)) {
|
|
PLOG(ERROR) << "write failed: " << DsuInstallDirFile(dsu_slot);
|
|
return INSTALL_ERROR_GENERIC;
|
|
}
|
|
return INSTALL_OK;
|
|
}
|
|
|
|
static bool IsExternalStoragePath(const std::string& path);
|
|
|
|
binder::Status GsiService::openInstall(const std::string& install_dir, int* _aidl_return) {
|
|
ENFORCE_SYSTEM;
|
|
std::lock_guard<std::mutex> guard(lock_);
|
|
if (IsGsiRunning()) {
|
|
*_aidl_return = IGsiService::INSTALL_ERROR_GENERIC;
|
|
return binder::Status::ok();
|
|
}
|
|
install_dir_ = install_dir;
|
|
if (int status = ValidateInstallParams(install_dir_)) {
|
|
*_aidl_return = status;
|
|
return binder::Status::ok();
|
|
}
|
|
std::string message;
|
|
auto dsu_slot = GetDsuSlot(install_dir_);
|
|
if (!RemoveFileIfExists(GetCompleteIndication(dsu_slot), &message)) {
|
|
LOG(ERROR) << message;
|
|
}
|
|
// Remember the installation directory before allocate any resource
|
|
*_aidl_return = SaveInstallation(install_dir_);
|
|
return binder::Status::ok();
|
|
}
|
|
|
|
binder::Status GsiService::closeInstall(int* _aidl_return) {
|
|
ENFORCE_SYSTEM;
|
|
std::lock_guard<std::mutex> guard(lock_);
|
|
auto dsu_slot = GetDsuSlot(install_dir_);
|
|
std::string file = GetCompleteIndication(dsu_slot);
|
|
if (!WriteStringToFile("OK", file)) {
|
|
PLOG(ERROR) << "write failed: " << file;
|
|
*_aidl_return = INSTALL_ERROR_GENERIC;
|
|
}
|
|
*_aidl_return = INSTALL_OK;
|
|
return binder::Status::ok();
|
|
}
|
|
|
|
binder::Status GsiService::createPartition(const ::std::string& name, int64_t size, bool readOnly,
|
|
int32_t* _aidl_return) {
|
|
ENFORCE_SYSTEM;
|
|
std::lock_guard<std::mutex> guard(lock_);
|
|
|
|
if (install_dir_.empty()) {
|
|
PLOG(ERROR) << "open is required for createPartition";
|
|
*_aidl_return = INSTALL_ERROR_GENERIC;
|
|
return binder::Status::ok();
|
|
}
|
|
|
|
// Do some precursor validation on the arguments before diving into the
|
|
// install process.
|
|
if (size % LP_SECTOR_SIZE) {
|
|
LOG(ERROR) << " size " << size << " is not a multiple of " << LP_SECTOR_SIZE;
|
|
*_aidl_return = INSTALL_ERROR_GENERIC;
|
|
return binder::Status::ok();
|
|
}
|
|
|
|
if (size == 0 && name == "userdata") {
|
|
size = kDefaultUserdataSize;
|
|
}
|
|
|
|
if (name == "userdata") {
|
|
auto dsu_slot = GetDsuSlot(install_dir_);
|
|
auto key_dir = DefaultDsuMetadataKeyDir(dsu_slot);
|
|
auto key_dir_file = DsuMetadataKeyDirFile(dsu_slot);
|
|
if (!android::base::WriteStringToFile(key_dir, key_dir_file)) {
|
|
PLOG(ERROR) << "write failed: " << key_dir_file;
|
|
*_aidl_return = INSTALL_ERROR_GENERIC;
|
|
return binder::Status::ok();
|
|
}
|
|
RestoreconMetadataFiles();
|
|
}
|
|
|
|
installer_ = std::make_unique<PartitionInstaller>(this, install_dir_, name,
|
|
GetDsuSlot(install_dir_), size, readOnly);
|
|
progress_ = {};
|
|
*_aidl_return = installer_->StartInstall();
|
|
return binder::Status::ok();
|
|
}
|
|
|
|
binder::Status GsiService::closePartition(int32_t* _aidl_return) {
|
|
ENFORCE_SYSTEM;
|
|
std::lock_guard<std::mutex> guard(lock_);
|
|
|
|
if (installer_ == nullptr) {
|
|
LOG(ERROR) << "createPartition() has to be called before closePartition()";
|
|
*_aidl_return = IGsiService::INSTALL_ERROR_GENERIC;
|
|
return binder::Status::ok();
|
|
}
|
|
// It is important to not reset |installer_| here because other methods such
|
|
// as enableGsi() relies on the state of |installer_|.
|
|
*_aidl_return = installer_->FinishInstall();
|
|
return binder::Status::ok();
|
|
}
|
|
|
|
binder::Status GsiService::commitGsiChunkFromStream(const android::os::ParcelFileDescriptor& stream,
|
|
int64_t bytes, bool* _aidl_return) {
|
|
ENFORCE_SYSTEM;
|
|
std::lock_guard<std::mutex> guard(lock_);
|
|
|
|
if (!installer_) {
|
|
*_aidl_return = false;
|
|
return binder::Status::ok();
|
|
}
|
|
|
|
*_aidl_return = installer_->CommitGsiChunk(stream.get(), bytes);
|
|
return binder::Status::ok();
|
|
}
|
|
|
|
void GsiService::StartAsyncOperation(const std::string& step, int64_t total_bytes) {
|
|
std::lock_guard<std::mutex> guard(progress_lock_);
|
|
|
|
progress_.step = step;
|
|
progress_.status = STATUS_WORKING;
|
|
progress_.bytes_processed = 0;
|
|
progress_.total_bytes = total_bytes;
|
|
}
|
|
|
|
void GsiService::UpdateProgress(int status, int64_t bytes_processed) {
|
|
std::lock_guard<std::mutex> guard(progress_lock_);
|
|
|
|
progress_.status = status;
|
|
if (status == STATUS_COMPLETE) {
|
|
progress_.bytes_processed = progress_.total_bytes;
|
|
} else {
|
|
progress_.bytes_processed = bytes_processed;
|
|
}
|
|
}
|
|
|
|
binder::Status GsiService::getInstallProgress(::android::gsi::GsiProgress* _aidl_return) {
|
|
ENFORCE_SYSTEM;
|
|
std::lock_guard<std::mutex> guard(progress_lock_);
|
|
|
|
if (installer_ == nullptr) {
|
|
progress_ = {};
|
|
}
|
|
*_aidl_return = progress_;
|
|
return binder::Status::ok();
|
|
}
|
|
|
|
binder::Status GsiService::commitGsiChunkFromAshmem(int64_t bytes, bool* _aidl_return) {
|
|
ENFORCE_SYSTEM;
|
|
std::lock_guard<std::mutex> guard(lock_);
|
|
|
|
if (!installer_) {
|
|
*_aidl_return = false;
|
|
return binder::Status::ok();
|
|
}
|
|
*_aidl_return = installer_->CommitGsiChunk(bytes);
|
|
return binder::Status::ok();
|
|
}
|
|
|
|
binder::Status GsiService::setGsiAshmem(const ::android::os::ParcelFileDescriptor& ashmem,
|
|
int64_t size, bool* _aidl_return) {
|
|
ENFORCE_SYSTEM;
|
|
if (!installer_) {
|
|
*_aidl_return = false;
|
|
return binder::Status::ok();
|
|
}
|
|
*_aidl_return = installer_->MapAshmem(ashmem.get(), size);
|
|
return binder::Status::ok();
|
|
}
|
|
|
|
binder::Status GsiService::enableGsiAsync(bool one_shot, const std::string& dsuSlot,
|
|
const sp<IGsiServiceCallback>& resultCallback) {
|
|
int result;
|
|
auto status = enableGsi(one_shot, dsuSlot, &result);
|
|
if (!status.isOk()) {
|
|
LOG(ERROR) << "Could not enableGsi: " << status.exceptionMessage().string();
|
|
result = IGsiService::INSTALL_ERROR_GENERIC;
|
|
}
|
|
resultCallback->onResult(result);
|
|
return binder::Status::ok();
|
|
}
|
|
|
|
binder::Status GsiService::enableGsi(bool one_shot, const std::string& dsuSlot, int* _aidl_return) {
|
|
std::lock_guard<std::mutex> guard(lock_);
|
|
|
|
if (!WriteStringToFile(dsuSlot, kDsuActiveFile)) {
|
|
PLOG(ERROR) << "write failed: " << GetDsuSlot(install_dir_);
|
|
*_aidl_return = INSTALL_ERROR_GENERIC;
|
|
return binder::Status::ok();
|
|
}
|
|
RestoreconMetadataFiles();
|
|
if (installer_) {
|
|
ENFORCE_SYSTEM;
|
|
installer_ = {};
|
|
// Note: create the install status file last, since this is the actual boot
|
|
// indicator.
|
|
if (!SetBootMode(one_shot) || !CreateInstallStatusFile()) {
|
|
*_aidl_return = IGsiService::INSTALL_ERROR_GENERIC;
|
|
} else {
|
|
*_aidl_return = INSTALL_OK;
|
|
}
|
|
} else {
|
|
ENFORCE_SYSTEM_OR_SHELL;
|
|
*_aidl_return = ReenableGsi(one_shot);
|
|
}
|
|
|
|
installer_ = nullptr;
|
|
return binder::Status::ok();
|
|
}
|
|
|
|
binder::Status GsiService::isGsiEnabled(bool* _aidl_return) {
|
|
ENFORCE_SYSTEM_OR_SHELL;
|
|
std::lock_guard<std::mutex> guard(lock_);
|
|
std::string boot_key;
|
|
if (!GetInstallStatus(&boot_key)) {
|
|
*_aidl_return = false;
|
|
} else {
|
|
*_aidl_return = (boot_key != kInstallStatusDisabled);
|
|
}
|
|
return binder::Status::ok();
|
|
}
|
|
|
|
binder::Status GsiService::removeGsiAsync(const sp<IGsiServiceCallback>& resultCallback) {
|
|
bool result;
|
|
auto status = removeGsi(&result);
|
|
if (!status.isOk()) {
|
|
LOG(ERROR) << "Could not removeGsi: " << status.exceptionMessage().string();
|
|
result = IGsiService::INSTALL_ERROR_GENERIC;
|
|
}
|
|
resultCallback->onResult(result);
|
|
return binder::Status::ok();
|
|
}
|
|
|
|
binder::Status GsiService::removeGsi(bool* _aidl_return) {
|
|
ENFORCE_SYSTEM_OR_SHELL;
|
|
std::lock_guard<std::mutex> guard(lock_);
|
|
|
|
std::string install_dir = GetActiveInstalledImageDir();
|
|
if (IsGsiRunning()) {
|
|
// Can't remove gsi files while running.
|
|
*_aidl_return = UninstallGsi();
|
|
} else {
|
|
installer_ = {};
|
|
*_aidl_return = RemoveGsiFiles(install_dir);
|
|
}
|
|
return binder::Status::ok();
|
|
}
|
|
|
|
binder::Status GsiService::disableGsi(bool* _aidl_return) {
|
|
ENFORCE_SYSTEM_OR_SHELL;
|
|
std::lock_guard<std::mutex> guard(lock_);
|
|
|
|
*_aidl_return = DisableGsiInstall();
|
|
return binder::Status::ok();
|
|
}
|
|
|
|
binder::Status GsiService::isGsiRunning(bool* _aidl_return) {
|
|
ENFORCE_SYSTEM_OR_SHELL;
|
|
std::lock_guard<std::mutex> guard(lock_);
|
|
|
|
*_aidl_return = IsGsiRunning();
|
|
return binder::Status::ok();
|
|
}
|
|
|
|
binder::Status GsiService::isGsiInstalled(bool* _aidl_return) {
|
|
ENFORCE_SYSTEM_OR_SHELL;
|
|
std::lock_guard<std::mutex> guard(lock_);
|
|
|
|
*_aidl_return = IsGsiInstalled();
|
|
return binder::Status::ok();
|
|
}
|
|
|
|
binder::Status GsiService::isGsiInstallInProgress(bool* _aidl_return) {
|
|
ENFORCE_SYSTEM_OR_SHELL;
|
|
std::lock_guard<std::mutex> guard(lock_);
|
|
|
|
*_aidl_return = !!installer_;
|
|
return binder::Status::ok();
|
|
}
|
|
|
|
binder::Status GsiService::cancelGsiInstall(bool* _aidl_return) {
|
|
ENFORCE_SYSTEM;
|
|
should_abort_ = true;
|
|
std::lock_guard<std::mutex> guard(lock_);
|
|
|
|
should_abort_ = false;
|
|
installer_ = nullptr;
|
|
|
|
*_aidl_return = true;
|
|
return binder::Status::ok();
|
|
}
|
|
|
|
binder::Status GsiService::getInstalledGsiImageDir(std::string* _aidl_return) {
|
|
ENFORCE_SYSTEM;
|
|
std::lock_guard<std::mutex> guard(lock_);
|
|
|
|
*_aidl_return = GetActiveInstalledImageDir();
|
|
return binder::Status::ok();
|
|
}
|
|
|
|
binder::Status GsiService::getActiveDsuSlot(std::string* _aidl_return) {
|
|
ENFORCE_SYSTEM_OR_SHELL;
|
|
std::lock_guard<std::mutex> guard(lock_);
|
|
|
|
*_aidl_return = GetActiveDsuSlot();
|
|
return binder::Status::ok();
|
|
}
|
|
|
|
binder::Status GsiService::getInstalledDsuSlots(std::vector<std::string>* _aidl_return) {
|
|
ENFORCE_SYSTEM;
|
|
std::lock_guard<std::mutex> guard(lock_);
|
|
*_aidl_return = GetInstalledDsuSlots();
|
|
return binder::Status::ok();
|
|
}
|
|
|
|
binder::Status GsiService::zeroPartition(const std::string& name, int* _aidl_return) {
|
|
ENFORCE_SYSTEM_OR_SHELL;
|
|
std::lock_guard<std::mutex> guard(lock_);
|
|
|
|
if (IsGsiRunning() || !IsGsiInstalled()) {
|
|
*_aidl_return = IGsiService::INSTALL_ERROR_GENERIC;
|
|
return binder::Status::ok();
|
|
}
|
|
|
|
std::string install_dir = GetActiveInstalledImageDir();
|
|
*_aidl_return = PartitionInstaller::WipeWritable(GetDsuSlot(install_dir), install_dir, name);
|
|
|
|
return binder::Status::ok();
|
|
}
|
|
|
|
static binder::Status BinderError(const std::string& message,
|
|
FiemapStatus::ErrorCode status = FiemapStatus::ErrorCode::ERROR) {
|
|
return binder::Status::fromServiceSpecificError(static_cast<int32_t>(status), message.c_str());
|
|
}
|
|
|
|
binder::Status GsiService::dumpDeviceMapperDevices(std::string* _aidl_return) {
|
|
ENFORCE_SYSTEM_OR_SHELL;
|
|
|
|
auto& dm = DeviceMapper::Instance();
|
|
|
|
std::vector<DeviceMapper::DmBlockDevice> devices;
|
|
if (!dm.GetAvailableDevices(&devices)) {
|
|
return BinderError("Could not list devices");
|
|
}
|
|
|
|
std::stringstream text;
|
|
for (const auto& device : devices) {
|
|
text << "Device " << device.name() << " (" << device.Major() << ":" << device.Minor()
|
|
<< ")\n";
|
|
|
|
std::vector<DeviceMapper::TargetInfo> table;
|
|
if (!dm.GetTableInfo(device.name(), &table)) {
|
|
continue;
|
|
}
|
|
|
|
for (const auto& target : table) {
|
|
const auto& spec = target.spec;
|
|
auto target_type = DeviceMapper::GetTargetType(spec);
|
|
text << " " << target_type << " " << spec.sector_start << " " << spec.length << " "
|
|
<< target.data << "\n";
|
|
}
|
|
}
|
|
|
|
*_aidl_return = text.str();
|
|
return binder::Status::ok();
|
|
}
|
|
|
|
binder::Status GsiService::getAvbPublicKey(AvbPublicKey* dst, int32_t* _aidl_return) {
|
|
ENFORCE_SYSTEM;
|
|
std::lock_guard<std::mutex> guard(lock_);
|
|
|
|
if (!installer_) {
|
|
*_aidl_return = INSTALL_ERROR_GENERIC;
|
|
return binder::Status::ok();
|
|
}
|
|
int fd = installer_->GetPartitionFd();
|
|
if (!GetAvbPublicKeyFromFd(fd, dst)) {
|
|
LOG(ERROR) << "Failed to extract AVB public key";
|
|
*_aidl_return = INSTALL_ERROR_GENERIC;
|
|
return binder::Status::ok();
|
|
}
|
|
*_aidl_return = INSTALL_OK;
|
|
return binder::Status::ok();
|
|
}
|
|
|
|
binder::Status GsiService::suggestScratchSize(int64_t* _aidl_return) {
|
|
ENFORCE_SYSTEM;
|
|
|
|
static constexpr uint64_t kMinScratchSize = 512_MiB;
|
|
static constexpr uint64_t kMaxScratchSize = 2_GiB;
|
|
|
|
uint64_t size = 0;
|
|
struct statvfs info;
|
|
if (statvfs(install_dir_.c_str(), &info)) {
|
|
PLOG(ERROR) << "Could not statvfs(" << install_dir_ << ")";
|
|
} else {
|
|
// Keep the storage device at least 40% free, plus 1% for jitter.
|
|
constexpr int jitter = 1;
|
|
const uint64_t reserved_blocks =
|
|
static_cast<uint64_t>(info.f_blocks) * (kMinimumFreeSpaceThreshold + jitter) / 100;
|
|
if (info.f_bavail > reserved_blocks) {
|
|
size = (info.f_bavail - reserved_blocks) * info.f_frsize;
|
|
}
|
|
}
|
|
|
|
// We can safely downcast the result here, since we clamped the result within int64_t range.
|
|
*_aidl_return = std::clamp(size, kMinScratchSize, kMaxScratchSize);
|
|
return binder::Status::ok();
|
|
}
|
|
|
|
bool GsiService::CreateInstallStatusFile() {
|
|
if (!android::base::WriteStringToFile("0", kDsuInstallStatusFile)) {
|
|
PLOG(ERROR) << "write " << kDsuInstallStatusFile;
|
|
return false;
|
|
}
|
|
SetProperty(kGsiInstalledProp, "1");
|
|
return true;
|
|
}
|
|
|
|
bool GsiService::SetBootMode(bool one_shot) {
|
|
if (one_shot) {
|
|
if (!android::base::WriteStringToFile("1", kDsuOneShotBootFile)) {
|
|
PLOG(ERROR) << "write " << kDsuOneShotBootFile;
|
|
return false;
|
|
}
|
|
} else if (!access(kDsuOneShotBootFile, F_OK)) {
|
|
std::string error;
|
|
if (!android::base::RemoveFileIfExists(kDsuOneShotBootFile, &error)) {
|
|
LOG(ERROR) << error;
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static binder::Status UidSecurityError() {
|
|
uid_t uid = IPCThreadState::self()->getCallingUid();
|
|
auto message = StringPrintf("UID %d is not allowed", uid);
|
|
return binder::Status::fromExceptionCode(binder::Status::EX_SECURITY, String8(message.c_str()));
|
|
}
|
|
|
|
class ImageService : public BinderService<ImageService>, public BnImageService {
|
|
public:
|
|
ImageService(GsiService* service, std::unique_ptr<ImageManager>&& impl, uid_t uid);
|
|
binder::Status getAllBackingImages(std::vector<std::string>* _aidl_return);
|
|
binder::Status createBackingImage(const std::string& name, int64_t size, int flags,
|
|
const sp<IProgressCallback>& on_progress) override;
|
|
binder::Status deleteBackingImage(const std::string& name) override;
|
|
binder::Status mapImageDevice(const std::string& name, int32_t timeout_ms,
|
|
MappedImage* mapping) override;
|
|
binder::Status unmapImageDevice(const std::string& name) override;
|
|
binder::Status backingImageExists(const std::string& name, bool* _aidl_return) override;
|
|
binder::Status isImageMapped(const std::string& name, bool* _aidl_return) override;
|
|
binder::Status getAvbPublicKey(const std::string& name, AvbPublicKey* dst,
|
|
int32_t* _aidl_return) override;
|
|
binder::Status zeroFillNewImage(const std::string& name, int64_t bytes) override;
|
|
binder::Status removeAllImages() override;
|
|
binder::Status removeDisabledImages() override;
|
|
binder::Status getMappedImageDevice(const std::string& name, std::string* device) override;
|
|
|
|
private:
|
|
bool CheckUid();
|
|
|
|
android::sp<GsiService> service_;
|
|
std::unique_ptr<ImageManager> impl_;
|
|
uid_t uid_;
|
|
};
|
|
|
|
ImageService::ImageService(GsiService* service, std::unique_ptr<ImageManager>&& impl, uid_t uid)
|
|
: service_(service), impl_(std::move(impl)), uid_(uid) {}
|
|
|
|
binder::Status ImageService::getAllBackingImages(std::vector<std::string>* _aidl_return) {
|
|
*_aidl_return = impl_->GetAllBackingImages();
|
|
return binder::Status::ok();
|
|
}
|
|
|
|
binder::Status ImageService::createBackingImage(const std::string& name, int64_t size, int flags,
|
|
const sp<IProgressCallback>& on_progress) {
|
|
if (!CheckUid()) return UidSecurityError();
|
|
|
|
std::lock_guard<std::mutex> guard(service_->lock());
|
|
|
|
std::function<bool(uint64_t, uint64_t)> callback;
|
|
if (on_progress) {
|
|
callback = [on_progress](uint64_t current, uint64_t total) -> bool {
|
|
auto status = on_progress->onProgress(static_cast<int64_t>(current),
|
|
static_cast<int64_t>(total));
|
|
if (!status.isOk()) {
|
|
LOG(ERROR) << "progress callback returned: " << status.toString8().string();
|
|
return false;
|
|
}
|
|
return true;
|
|
};
|
|
}
|
|
|
|
auto res = impl_->CreateBackingImage(name, size, flags, std::move(callback));
|
|
if (!res.is_ok()) {
|
|
return BinderError("Failed to create: " + res.string(), res.error_code());
|
|
}
|
|
return binder::Status::ok();
|
|
}
|
|
|
|
binder::Status ImageService::deleteBackingImage(const std::string& name) {
|
|
if (!CheckUid()) return UidSecurityError();
|
|
|
|
std::lock_guard<std::mutex> guard(service_->lock());
|
|
|
|
if (!impl_->DeleteBackingImage(name)) {
|
|
return BinderError("Failed to delete");
|
|
}
|
|
return binder::Status::ok();
|
|
}
|
|
|
|
binder::Status ImageService::mapImageDevice(const std::string& name, int32_t timeout_ms,
|
|
MappedImage* mapping) {
|
|
if (!CheckUid()) return UidSecurityError();
|
|
|
|
std::lock_guard<std::mutex> guard(service_->lock());
|
|
|
|
if (!impl_->MapImageDevice(name, std::chrono::milliseconds(timeout_ms), &mapping->path)) {
|
|
return BinderError("Failed to map");
|
|
}
|
|
return binder::Status::ok();
|
|
}
|
|
|
|
binder::Status ImageService::unmapImageDevice(const std::string& name) {
|
|
if (!CheckUid()) return UidSecurityError();
|
|
|
|
std::lock_guard<std::mutex> guard(service_->lock());
|
|
|
|
if (!impl_->UnmapImageDevice(name)) {
|
|
return BinderError("Failed to unmap");
|
|
}
|
|
return binder::Status::ok();
|
|
}
|
|
|
|
binder::Status ImageService::backingImageExists(const std::string& name, bool* _aidl_return) {
|
|
if (!CheckUid()) return UidSecurityError();
|
|
|
|
std::lock_guard<std::mutex> guard(service_->lock());
|
|
|
|
*_aidl_return = impl_->BackingImageExists(name);
|
|
return binder::Status::ok();
|
|
}
|
|
|
|
binder::Status ImageService::isImageMapped(const std::string& name, bool* _aidl_return) {
|
|
if (!CheckUid()) return UidSecurityError();
|
|
|
|
std::lock_guard<std::mutex> guard(service_->lock());
|
|
|
|
*_aidl_return = impl_->IsImageMapped(name);
|
|
return binder::Status::ok();
|
|
}
|
|
|
|
binder::Status ImageService::getAvbPublicKey(const std::string& name, AvbPublicKey* dst,
|
|
int32_t* _aidl_return) {
|
|
if (!CheckUid()) return UidSecurityError();
|
|
|
|
std::lock_guard<std::mutex> guard(service_->lock());
|
|
|
|
std::string device_path;
|
|
std::unique_ptr<MappedDevice> mapped_device;
|
|
if (!impl_->IsImageMapped(name)) {
|
|
mapped_device = MappedDevice::Open(impl_.get(), 10s, name);
|
|
if (!mapped_device) {
|
|
PLOG(ERROR) << "Fail to map image: " << name;
|
|
*_aidl_return = IMAGE_ERROR;
|
|
return binder::Status::ok();
|
|
}
|
|
device_path = mapped_device->path();
|
|
} else {
|
|
if (!impl_->GetMappedImageDevice(name, &device_path)) {
|
|
PLOG(ERROR) << "GetMappedImageDevice() failed";
|
|
*_aidl_return = IMAGE_ERROR;
|
|
return binder::Status::ok();
|
|
}
|
|
}
|
|
android::base::unique_fd fd(open(device_path.c_str(), O_RDONLY | O_CLOEXEC));
|
|
if (!fd.ok()) {
|
|
PLOG(ERROR) << "Fail to open mapped device: " << device_path;
|
|
*_aidl_return = IMAGE_ERROR;
|
|
return binder::Status::ok();
|
|
}
|
|
bool ok = GetAvbPublicKeyFromFd(fd.get(), dst);
|
|
fd = {};
|
|
if (!ok) {
|
|
LOG(ERROR) << "Failed to extract AVB public key";
|
|
*_aidl_return = IMAGE_ERROR;
|
|
return binder::Status::ok();
|
|
}
|
|
*_aidl_return = IMAGE_OK;
|
|
return binder::Status::ok();
|
|
}
|
|
|
|
binder::Status ImageService::zeroFillNewImage(const std::string& name, int64_t bytes) {
|
|
if (!CheckUid()) return UidSecurityError();
|
|
|
|
std::lock_guard<std::mutex> guard(service_->lock());
|
|
|
|
if (bytes < 0) {
|
|
return BinderError("Cannot use negative values");
|
|
}
|
|
auto res = impl_->ZeroFillNewImage(name, bytes);
|
|
if (!res.is_ok()) {
|
|
return BinderError("Failed to fill image with zeros: " + res.string(), res.error_code());
|
|
}
|
|
return binder::Status::ok();
|
|
}
|
|
|
|
binder::Status ImageService::removeAllImages() {
|
|
if (!CheckUid()) return UidSecurityError();
|
|
|
|
std::lock_guard<std::mutex> guard(service_->lock());
|
|
if (!impl_->RemoveAllImages()) {
|
|
return BinderError("Failed to remove all images");
|
|
}
|
|
return binder::Status::ok();
|
|
}
|
|
|
|
binder::Status ImageService::removeDisabledImages() {
|
|
if (!CheckUid()) return UidSecurityError();
|
|
|
|
std::lock_guard<std::mutex> guard(service_->lock());
|
|
if (!impl_->RemoveDisabledImages()) {
|
|
return BinderError("Failed to remove disabled images");
|
|
}
|
|
return binder::Status::ok();
|
|
}
|
|
|
|
binder::Status ImageService::getMappedImageDevice(const std::string& name, std::string* device) {
|
|
if (!CheckUid()) return UidSecurityError();
|
|
|
|
std::lock_guard<std::mutex> guard(service_->lock());
|
|
if (!impl_->GetMappedImageDevice(name, device)) {
|
|
*device = "";
|
|
}
|
|
return binder::Status::ok();
|
|
}
|
|
|
|
bool ImageService::CheckUid() {
|
|
return uid_ == IPCThreadState::self()->getCallingUid();
|
|
}
|
|
|
|
binder::Status GsiService::openImageService(const std::string& prefix,
|
|
android::sp<IImageService>* _aidl_return) {
|
|
using android::base::StartsWith;
|
|
|
|
static constexpr char kImageMetadataPrefix[] = "/metadata/gsi/";
|
|
static constexpr char kImageDataPrefix[] = "/data/gsi/";
|
|
|
|
auto in_metadata_dir = kImageMetadataPrefix + prefix;
|
|
auto in_data_dir = kImageDataPrefix + prefix;
|
|
auto install_dir_file = DsuInstallDirFile(GetDsuSlot(prefix));
|
|
|
|
std::string in_data_dir_tmp;
|
|
if (android::base::ReadFileToString(install_dir_file, &in_data_dir_tmp)) {
|
|
in_data_dir = in_data_dir_tmp;
|
|
LOG(INFO) << "load " << install_dir_file << ":" << in_data_dir;
|
|
}
|
|
std::string metadata_dir, data_dir;
|
|
if (!android::base::Realpath(in_metadata_dir, &metadata_dir)) {
|
|
PLOG(ERROR) << "realpath failed for metadata: " << in_metadata_dir;
|
|
return BinderError("Invalid path");
|
|
}
|
|
if (!android::base::Realpath(in_data_dir, &data_dir)) {
|
|
PLOG(ERROR) << "realpath failed for data: " << in_data_dir;
|
|
return BinderError("Invalid path");
|
|
}
|
|
if (!StartsWith(metadata_dir, kImageMetadataPrefix)) {
|
|
return BinderError("Invalid metadata path");
|
|
}
|
|
if (!StartsWith(data_dir, kImageDataPrefix) && !StartsWith(data_dir, kDsuSDPrefix)) {
|
|
return BinderError("Invalid data path");
|
|
}
|
|
|
|
uid_t uid = IPCThreadState::self()->getCallingUid();
|
|
if (uid != AID_ROOT) {
|
|
return UidSecurityError();
|
|
}
|
|
|
|
auto impl = ImageManager::Open(metadata_dir, data_dir);
|
|
if (!impl) {
|
|
return BinderError("Unknown error");
|
|
}
|
|
|
|
*_aidl_return = new ImageService(this, std::move(impl), uid);
|
|
return binder::Status::ok();
|
|
}
|
|
|
|
binder::Status GsiService::CheckUid(AccessLevel level) {
|
|
std::vector<uid_t> allowed_uids{AID_ROOT, AID_SYSTEM};
|
|
if (level == AccessLevel::SystemOrShell) {
|
|
allowed_uids.push_back(AID_SHELL);
|
|
}
|
|
|
|
uid_t uid = IPCThreadState::self()->getCallingUid();
|
|
for (const auto& allowed_uid : allowed_uids) {
|
|
if (allowed_uid == uid) {
|
|
return binder::Status::ok();
|
|
}
|
|
}
|
|
return UidSecurityError();
|
|
}
|
|
|
|
static bool IsExternalStoragePath(const std::string& path) {
|
|
if (!android::base::StartsWith(path, kDsuSDPrefix)) {
|
|
return false;
|
|
}
|
|
unique_fd fd(open(path.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW));
|
|
if (fd < 0) {
|
|
PLOG(ERROR) << "open failed: " << path;
|
|
return false;
|
|
}
|
|
struct statfs info;
|
|
if (fstatfs(fd, &info)) {
|
|
PLOG(ERROR) << "statfs failed: " << path;
|
|
return false;
|
|
}
|
|
LOG(ERROR) << "fs type: " << info.f_type;
|
|
return info.f_type == MSDOS_SUPER_MAGIC;
|
|
}
|
|
|
|
int GsiService::ValidateInstallParams(std::string& install_dir) {
|
|
// If no install path was specified, use the default path. We also allow
|
|
// specifying the top-level folder, and then we choose the correct location
|
|
// underneath.
|
|
if (install_dir.empty() || install_dir == "/data/gsi") {
|
|
install_dir = kDefaultDsuImageFolder;
|
|
}
|
|
|
|
if (access(install_dir.c_str(), F_OK) != 0 && (errno == ENOENT)) {
|
|
if (android::base::StartsWith(install_dir, kDsuSDPrefix)) {
|
|
if (mkdir(install_dir.c_str(), 0755) != 0) {
|
|
PLOG(ERROR) << "Failed to create " << install_dir;
|
|
return INSTALL_ERROR_GENERIC;
|
|
}
|
|
}
|
|
}
|
|
// Normalize the path and add a trailing slash.
|
|
std::string origInstallDir = install_dir;
|
|
if (!android::base::Realpath(origInstallDir, &install_dir)) {
|
|
PLOG(ERROR) << "realpath failed: " << origInstallDir;
|
|
return INSTALL_ERROR_GENERIC;
|
|
}
|
|
// Ensure the path ends in / for consistency.
|
|
if (!android::base::EndsWith(install_dir, "/")) {
|
|
install_dir += "/";
|
|
}
|
|
|
|
// Currently, we can only install to /data/gsi/ or external storage.
|
|
if (IsExternalStoragePath(install_dir)) {
|
|
Fstab fstab;
|
|
if (!ReadDefaultFstab(&fstab)) {
|
|
LOG(ERROR) << "cannot read default fstab";
|
|
return INSTALL_ERROR_GENERIC;
|
|
}
|
|
FstabEntry* system = GetEntryForMountPoint(&fstab, "/system");
|
|
if (!system) {
|
|
LOG(ERROR) << "cannot find /system fstab entry";
|
|
return INSTALL_ERROR_GENERIC;
|
|
}
|
|
if (fs_mgr_verity_is_check_at_most_once(*system)) {
|
|
LOG(ERROR) << "cannot install GSIs to external media if verity uses check_at_most_once";
|
|
return INSTALL_ERROR_GENERIC;
|
|
}
|
|
} else if (install_dir != kDefaultDsuImageFolder) {
|
|
LOG(ERROR) << "cannot install DSU to " << install_dir;
|
|
return INSTALL_ERROR_GENERIC;
|
|
}
|
|
return INSTALL_OK;
|
|
}
|
|
|
|
std::string GsiService::GetActiveDsuSlot() {
|
|
if (!install_dir_.empty()) {
|
|
return GetDsuSlot(install_dir_);
|
|
} else {
|
|
std::string active_dsu;
|
|
return GetActiveDsu(&active_dsu) ? active_dsu : "";
|
|
}
|
|
}
|
|
|
|
std::string GsiService::GetActiveInstalledImageDir() {
|
|
// Just in case an install was left hanging.
|
|
if (installer_) {
|
|
return installer_->install_dir();
|
|
} else {
|
|
return GetInstalledImageDir();
|
|
}
|
|
}
|
|
|
|
std::string GsiService::GetInstalledImageDir() {
|
|
// If there's no install left, just return /data/gsi since that's where
|
|
// installs go by default.
|
|
std::string active_dsu;
|
|
std::string dir;
|
|
if (GetActiveDsu(&active_dsu) &&
|
|
android::base::ReadFileToString(DsuInstallDirFile(active_dsu), &dir)) {
|
|
return dir;
|
|
}
|
|
return kDefaultDsuImageFolder;
|
|
}
|
|
|
|
int GsiService::ReenableGsi(bool one_shot) {
|
|
if (!android::gsi::IsGsiInstalled()) {
|
|
LOG(ERROR) << "no gsi installed - cannot re-enable";
|
|
return INSTALL_ERROR_GENERIC;
|
|
}
|
|
std::string boot_key;
|
|
if (!GetInstallStatus(&boot_key)) {
|
|
PLOG(ERROR) << "read " << kDsuInstallStatusFile;
|
|
return INSTALL_ERROR_GENERIC;
|
|
}
|
|
if (boot_key != kInstallStatusDisabled) {
|
|
LOG(ERROR) << "GSI is not currently disabled";
|
|
return INSTALL_ERROR_GENERIC;
|
|
}
|
|
if (IsGsiRunning()) {
|
|
if (!SetBootMode(one_shot) || !CreateInstallStatusFile()) {
|
|
return IGsiService::INSTALL_ERROR_GENERIC;
|
|
}
|
|
return IGsiService::INSTALL_OK;
|
|
}
|
|
if (!SetBootMode(one_shot) || !CreateInstallStatusFile()) {
|
|
return IGsiService::INSTALL_ERROR_GENERIC;
|
|
}
|
|
return IGsiService::INSTALL_OK;
|
|
}
|
|
|
|
static android::sp<android::os::IVold> GetVoldService() {
|
|
return android::waitForService<android::os::IVold>(android::String16("vold"));
|
|
}
|
|
|
|
bool GsiService::RemoveGsiFiles(const std::string& install_dir) {
|
|
bool ok = true;
|
|
auto active_dsu = GetDsuSlot(install_dir);
|
|
if (auto manager = ImageManager::Open(MetadataDir(active_dsu), install_dir)) {
|
|
std::vector<std::string> images = manager->GetAllBackingImages();
|
|
for (auto&& image : images) {
|
|
if (!android::base::EndsWith(image, kDsuPostfix)) {
|
|
continue;
|
|
}
|
|
if (manager->IsImageMapped(image)) {
|
|
ok &= manager->UnmapImageDevice(image);
|
|
}
|
|
ok &= manager->DeleteBackingImage(image);
|
|
}
|
|
}
|
|
auto dsu_slot = GetDsuSlot(install_dir);
|
|
std::vector<std::string> files{
|
|
kDsuInstallStatusFile,
|
|
kDsuOneShotBootFile,
|
|
DsuInstallDirFile(dsu_slot),
|
|
GetCompleteIndication(dsu_slot),
|
|
};
|
|
for (const auto& file : files) {
|
|
std::string message;
|
|
if (!RemoveFileIfExists(file, &message)) {
|
|
LOG(ERROR) << message;
|
|
ok = false;
|
|
}
|
|
}
|
|
if (auto vold = GetVoldService()) {
|
|
auto status = vold->destroyDsuMetadataKey(dsu_slot);
|
|
if (status.isOk()) {
|
|
std::string message;
|
|
if (!RemoveFileIfExists(DsuMetadataKeyDirFile(dsu_slot), &message)) {
|
|
LOG(ERROR) << message;
|
|
ok = false;
|
|
}
|
|
} else {
|
|
LOG(ERROR) << "Failed to destroy DSU metadata encryption key.";
|
|
ok = false;
|
|
}
|
|
} else {
|
|
LOG(ERROR) << "Failed to retrieve vold service.";
|
|
ok = false;
|
|
}
|
|
if (ok) {
|
|
SetProperty(kGsiInstalledProp, "0");
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
bool GsiService::DisableGsiInstall() {
|
|
if (!android::gsi::IsGsiInstalled()) {
|
|
LOG(ERROR) << "cannot disable gsi install - no install detected";
|
|
return false;
|
|
}
|
|
if (installer_) {
|
|
LOG(ERROR) << "cannot disable gsi during GSI installation";
|
|
return false;
|
|
}
|
|
if (!DisableGsi()) {
|
|
PLOG(ERROR) << "could not write gsi status";
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
std::string GsiService::GetCompleteIndication(const std::string& dsu_slot) {
|
|
return DSU_METADATA_PREFIX + dsu_slot + "/complete";
|
|
}
|
|
|
|
bool GsiService::IsInstallationComplete(const std::string& dsu_slot) {
|
|
if (access(kDsuInstallStatusFile, F_OK) != 0) {
|
|
return false;
|
|
}
|
|
std::string file = GetCompleteIndication(dsu_slot);
|
|
std::string content;
|
|
if (!ReadFileToString(file, &content)) {
|
|
return false;
|
|
}
|
|
return content == "OK";
|
|
}
|
|
|
|
std::vector<std::string> GsiService::GetInstalledDsuSlots() {
|
|
std::vector<std::string> dsu_slots;
|
|
auto d = std::unique_ptr<DIR, decltype(&closedir)>(opendir(DSU_METADATA_PREFIX), closedir);
|
|
if (d != nullptr) {
|
|
struct dirent* de;
|
|
while ((de = readdir(d.get())) != nullptr) {
|
|
if (de->d_name[0] == '.') {
|
|
continue;
|
|
}
|
|
auto dsu_slot = std::string(de->d_name);
|
|
if (access(DsuInstallDirFile(dsu_slot).c_str(), F_OK) != 0) {
|
|
continue;
|
|
}
|
|
dsu_slots.push_back(dsu_slot);
|
|
}
|
|
}
|
|
return dsu_slots;
|
|
}
|
|
|
|
void GsiService::CleanCorruptedInstallation() {
|
|
for (auto&& slot : GetInstalledDsuSlots()) {
|
|
bool is_complete = IsInstallationComplete(slot);
|
|
if (!is_complete) {
|
|
LOG(INFO) << "CleanCorruptedInstallation for slot: " << slot;
|
|
std::string install_dir;
|
|
if (!android::base::ReadFileToString(DsuInstallDirFile(slot), &install_dir) ||
|
|
!RemoveGsiFiles(install_dir)) {
|
|
LOG(ERROR) << "Failed to CleanCorruptedInstallation on " << slot;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void GsiService::RunStartupTasks() {
|
|
CleanCorruptedInstallation();
|
|
|
|
std::string active_dsu;
|
|
if (!GetActiveDsu(&active_dsu)) {
|
|
PLOG(INFO) << "no DSU";
|
|
return;
|
|
}
|
|
std::string boot_key;
|
|
if (!GetInstallStatus(&boot_key)) {
|
|
PLOG(ERROR) << "read " << kDsuInstallStatusFile;
|
|
return;
|
|
}
|
|
|
|
if (!IsGsiRunning()) {
|
|
// Check if a wipe was requested from fastboot or adb-in-gsi.
|
|
if (boot_key == kInstallStatusWipe) {
|
|
RemoveGsiFiles(GetInstalledImageDir());
|
|
}
|
|
} else {
|
|
// NB: When single-boot is enabled, init will write "disabled" into the
|
|
// install_status file, which will cause GetBootAttempts to return
|
|
// false. Thus, we won't write "ok" here.
|
|
int ignore;
|
|
if (GetBootAttempts(boot_key, &ignore)) {
|
|
// Mark the GSI as having successfully booted.
|
|
if (!android::base::WriteStringToFile(kInstallStatusOk, kDsuInstallStatusFile)) {
|
|
PLOG(ERROR) << "write " << kDsuInstallStatusFile;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool GetAvbPublicKeyFromFd(int fd, AvbPublicKey* dst) {
|
|
// Read the AVB footer from EOF.
|
|
int64_t total_size = get_block_device_size(fd);
|
|
int64_t footer_offset = total_size - AVB_FOOTER_SIZE;
|
|
std::array<uint8_t, AVB_FOOTER_SIZE> footer_bytes;
|
|
if (!ReadFullyAtOffset(fd, footer_bytes.data(), AVB_FOOTER_SIZE, footer_offset)) {
|
|
PLOG(ERROR) << "cannot read AVB footer";
|
|
return false;
|
|
}
|
|
// Validate the AVB footer data and byte swap to native byte order.
|
|
AvbFooter footer;
|
|
if (!avb_footer_validate_and_byteswap((const AvbFooter*)footer_bytes.data(), &footer)) {
|
|
LOG(ERROR) << "invalid AVB footer";
|
|
return false;
|
|
}
|
|
// Read the VBMeta image.
|
|
std::vector<uint8_t> vbmeta_bytes(footer.vbmeta_size);
|
|
if (!ReadFullyAtOffset(fd, vbmeta_bytes.data(), vbmeta_bytes.size(), footer.vbmeta_offset)) {
|
|
PLOG(ERROR) << "cannot read VBMeta image";
|
|
return false;
|
|
}
|
|
// Validate the VBMeta image and retrieve AVB public key.
|
|
// After a successful call to avb_vbmeta_image_verify(), public_key_data
|
|
// will point to the serialized AVB public key, in the same format generated
|
|
// by the `avbtool extract_public_key` command.
|
|
const uint8_t* public_key_data;
|
|
size_t public_key_size;
|
|
AvbVBMetaVerifyResult result = avb_vbmeta_image_verify(vbmeta_bytes.data(), vbmeta_bytes.size(),
|
|
&public_key_data, &public_key_size);
|
|
if (result != AVB_VBMETA_VERIFY_RESULT_OK) {
|
|
LOG(ERROR) << "invalid VBMeta image: " << avb_vbmeta_verify_result_to_string(result);
|
|
return false;
|
|
}
|
|
if (public_key_data != nullptr) {
|
|
dst->bytes.resize(public_key_size);
|
|
memcpy(dst->bytes.data(), public_key_data, public_key_size);
|
|
dst->sha1.resize(SHA_DIGEST_LENGTH);
|
|
SHA1(public_key_data, public_key_size, dst->sha1.data());
|
|
}
|
|
return true;
|
|
}
|
|
|
|
} // namespace gsi
|
|
} // namespace android
|