/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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(const std::string& name) { auto lazyRegistrar = LazyServiceRegistrar::getInstance(); android::sp service = new GsiService(); auto ret = lazyRegistrar.registerService(service, name); 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 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 guard(lock_); installer_ = {}; auto dsu_slot = GetDsuSlot(install_dir_); std::string file = GetCompleteIndication(dsu_slot); if (!WriteStringToFile("OK", file)) { PLOG(ERROR) << "write failed: " << file; *_aidl_return = IGsiService::INSTALL_ERROR_GENERIC; return binder::Status::ok(); } // Create installation complete marker files, but set disabled immediately. if (!WriteStringToFile(dsu_slot, kDsuActiveFile)) { PLOG(ERROR) << "cannot write active DSU slot (" << dsu_slot << "): " << kDsuActiveFile; *_aidl_return = IGsiService::INSTALL_ERROR_GENERIC; return binder::Status::ok(); } RestoreconMetadataFiles(); // DisableGsi() creates the DSU install status file and mark it as "disabled". if (!DisableGsi()) { PLOG(ERROR) << "cannot write DSU status file: " << kDsuInstallStatusFile; *_aidl_return = IGsiService::INSTALL_ERROR_GENERIC; return binder::Status::ok(); } SetProperty(kGsiInstalledProp, "1"); *_aidl_return = IGsiService::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 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(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 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 isGsiInstallInProgress() relies on the state of |installer_|. // TODO: Maybe don't do this, use a dedicated |in_progress_| flag? *_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 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 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 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 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 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& resultCallback) { ENFORCE_SYSTEM_OR_SHELL; std::lock_guard guard(lock_); const auto result = EnableGsi(one_shot, dsuSlot); resultCallback->onResult(result); return binder::Status::ok(); } binder::Status GsiService::enableGsi(bool one_shot, const std::string& dsuSlot, int* _aidl_return) { ENFORCE_SYSTEM_OR_SHELL; std::lock_guard guard(lock_); *_aidl_return = EnableGsi(one_shot, dsuSlot); return binder::Status::ok(); } binder::Status GsiService::isGsiEnabled(bool* _aidl_return) { ENFORCE_SYSTEM_OR_SHELL; std::lock_guard 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& resultCallback) { bool result = false; 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 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 guard(lock_); *_aidl_return = DisableGsiInstall(); return binder::Status::ok(); } binder::Status GsiService::isGsiRunning(bool* _aidl_return) { ENFORCE_SYSTEM_OR_SHELL; std::lock_guard guard(lock_); *_aidl_return = IsGsiRunning(); return binder::Status::ok(); } binder::Status GsiService::isGsiInstalled(bool* _aidl_return) { ENFORCE_SYSTEM_OR_SHELL; std::lock_guard guard(lock_); *_aidl_return = IsGsiInstalled(); return binder::Status::ok(); } binder::Status GsiService::isGsiInstallInProgress(bool* _aidl_return) { ENFORCE_SYSTEM_OR_SHELL; std::lock_guard guard(lock_); *_aidl_return = !!installer_; return binder::Status::ok(); } binder::Status GsiService::cancelGsiInstall(bool* _aidl_return) { ENFORCE_SYSTEM; should_abort_ = true; std::lock_guard 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 guard(lock_); *_aidl_return = GetActiveInstalledImageDir(); return binder::Status::ok(); } binder::Status GsiService::getActiveDsuSlot(std::string* _aidl_return) { ENFORCE_SYSTEM_OR_SHELL; std::lock_guard guard(lock_); *_aidl_return = GetActiveDsuSlot(); return binder::Status::ok(); } binder::Status GsiService::getInstalledDsuSlots(std::vector* _aidl_return) { ENFORCE_SYSTEM; std::lock_guard 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 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(status), message.c_str()); } binder::Status GsiService::dumpDeviceMapperDevices(std::string* _aidl_return) { ENFORCE_SYSTEM_OR_SHELL; auto& dm = DeviceMapper::Instance(); std::vector 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 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 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 { uint64_t free_space = static_cast(info.f_bavail) * info.f_frsize; const auto free_space_threshold = PartitionInstaller::GetMinimumFreeSpaceThreshold(install_dir_); if (free_space_threshold.has_value() && free_space > *free_space_threshold) { // Round down to multiples of filesystem block size. size = (free_space - *free_space_threshold) / info.f_frsize * 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::ResetBootAttemptCounter() { 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, public BnImageService { public: ImageService(GsiService* service, std::unique_ptr&& impl, uid_t uid); binder::Status getAllBackingImages(std::vector* _aidl_return); binder::Status createBackingImage(const std::string& name, int64_t size, int flags, const sp& 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 disableImage(const std::string& name) override; binder::Status removeDisabledImages() override; binder::Status getMappedImageDevice(const std::string& name, std::string* device) override; binder::Status isImageDisabled(const std::string& name, bool* _aidl_return) override; private: bool CheckUid(); android::sp service_; std::unique_ptr impl_; uid_t uid_; }; ImageService::ImageService(GsiService* service, std::unique_ptr&& impl, uid_t uid) : service_(service), impl_(std::move(impl)), uid_(uid) {} binder::Status ImageService::getAllBackingImages(std::vector* _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& on_progress) { if (!CheckUid()) return UidSecurityError(); std::lock_guard guard(service_->lock()); std::function callback; if (on_progress) { callback = [on_progress](uint64_t current, uint64_t total) -> bool { auto status = on_progress->onProgress(static_cast(current), static_cast(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 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 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 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 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 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 guard(service_->lock()); std::string device_path; std::unique_ptr 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 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 guard(service_->lock()); if (!impl_->RemoveAllImages()) { return BinderError("Failed to remove all images"); } return binder::Status::ok(); } binder::Status ImageService::disableImage(const std::string& name) { if (!CheckUid()) return UidSecurityError(); std::lock_guard guard(service_->lock()); if (!impl_->DisableImage(name)) { return BinderError("Failed to disable image: " + name); } return binder::Status::ok(); } binder::Status ImageService::removeDisabledImages() { if (!CheckUid()) return UidSecurityError(); std::lock_guard guard(service_->lock()); if (!impl_->RemoveDisabledImages()) { return BinderError("Failed to remove disabled images"); } return binder::Status::ok(); } binder::Status ImageService::isImageDisabled(const std::string& name, bool* _aidl_return) { if (!CheckUid()) return UidSecurityError(); std::lock_guard guard(service_->lock()); *_aidl_return = impl_->IsImageDisabled(name); return binder::Status::ok(); } binder::Status ImageService::getMappedImageDevice(const std::string& name, std::string* device) { if (!CheckUid()) return UidSecurityError(); std::lock_guard 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* _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 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; } static android::sp GetVoldService() { return android::waitForService(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 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 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; } int GsiService::EnableGsi(bool one_shot, const std::string& dsu_slot) { if (!android::gsi::IsGsiInstalled()) { LOG(ERROR) << "no gsi installed - cannot enable"; return IGsiService::INSTALL_ERROR_GENERIC; } if (installer_) { LOG(ERROR) << "cannot enable an ongoing installation, was closeInstall() called?"; return IGsiService::INSTALL_ERROR_GENERIC; } if (!DisableGsi()) { PLOG(ERROR) << "cannot write DSU status file"; return IGsiService::INSTALL_ERROR_GENERIC; } if (!SetBootMode(one_shot)) { return IGsiService::INSTALL_ERROR_GENERIC; } if (!ResetBootAttemptCounter()) { return IGsiService::INSTALL_ERROR_GENERIC; } if (!WriteStringToFile(dsu_slot, kDsuActiveFile)) { PLOG(ERROR) << "cannot write active DSU slot (" << dsu_slot << "): " << kDsuActiveFile; return IGsiService::INSTALL_ERROR_GENERIC; } RestoreconMetadataFiles(); return IGsiService::INSTALL_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 GsiService::GetInstalledDsuSlots() { std::vector dsu_slots; auto d = std::unique_ptr(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; } } } } void GsiService::VerifyImageMaps() { std::vector> paths = { {"/metadata/gsi/remount", "/data/gsi/remount"}, {"/metadata/gsi/ota", "/data/gsi/ota"}, }; for (const auto& [metadata_dir, data_dir] : paths) { auto impl = ImageManager::Open(metadata_dir, data_dir); if (!impl) { LOG(ERROR) << "Could not open ImageManager for " << metadata_dir << " and " << data_dir; continue; } if (!impl->ValidateImageMaps()) { LOG(ERROR) << "ImageManager for " << metadata_dir << " failed validation, device data is at risk. Rebooting."; android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,fastboot"); continue; } LOG(INFO) << "ImageManager verification passed for " << metadata_dir; } } 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 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 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