| // Copyright 2018 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "dlcservice/dlc_service.h" |
| |
| #include <algorithm> |
| #include <memory> |
| #include <utility> |
| #include <vector> |
| |
| #include <base/files/file_enumerator.h> |
| #include <base/files/file_util.h> |
| #include <base/files/scoped_temp_dir.h> |
| #include <brillo/errors/error.h> |
| #include <brillo/errors/error_codes.h> |
| #include <chromeos/dbus/service_constants.h> |
| #include <dbus/dlcservice/dbus-constants.h> |
| |
| #include "dlcservice/boot_slot.h" |
| #include "dlcservice/utils.h" |
| |
| using base::Callback; |
| using base::File; |
| using base::FilePath; |
| using base::ScopedTempDir; |
| using brillo::MessageLoop; |
| using std::pair; |
| using std::string; |
| using std::unique_ptr; |
| using std::vector; |
| using update_engine::Operation; |
| using update_engine::StatusResult; |
| |
| namespace dlcservice { |
| |
| namespace { |
| |
| // Permissions for DLC module directories. |
| constexpr mode_t kDlcModuleDirectoryPerms = 0755; |
| |
| // Creates a directory with permissions required for DLC modules. |
| bool CreateDirWithDlcPermissions(const FilePath& path) { |
| File::Error file_err; |
| if (!base::CreateDirectoryAndGetError(path, &file_err)) { |
| LOG(ERROR) << "Failed to create directory '" << path.value() |
| << "': " << File::ErrorToString(file_err); |
| return false; |
| } |
| if (!base::SetPosixFilePermissions(path, kDlcModuleDirectoryPerms)) { |
| LOG(ERROR) << "Failed to set directory permissions for '" << path.value() |
| << "'"; |
| return false; |
| } |
| return true; |
| } |
| |
| // Creates a directory with an empty image file and resizes it to the given |
| // size. |
| bool CreateImageFile(const FilePath& path, int64_t image_size) { |
| if (!CreateDirWithDlcPermissions(path.DirName())) { |
| return false; |
| } |
| constexpr uint32_t file_flags = |
| File::FLAG_CREATE | File::FLAG_READ | File::FLAG_WRITE; |
| File file(path, file_flags); |
| if (!file.IsValid()) { |
| LOG(ERROR) << "Failed to create image file '" << path.value() |
| << "': " << File::ErrorToString(file.error_details()); |
| return false; |
| } |
| if (!file.SetLength(image_size)) { |
| LOG(ERROR) << "Failed to reserve backup file for DLC module image '" |
| << path.value() << "'"; |
| return false; |
| } |
| return true; |
| } |
| |
| // Sets the D-Bus error object with error code and error message after which |
| // logs the error message will also be logged. |
| void LogAndSetError(brillo::ErrorPtr* err, |
| const string& code, |
| const string& msg) { |
| if (err) |
| *err = brillo::Error::Create(FROM_HERE, brillo::errors::dbus::kDomain, code, |
| msg); |
| LOG(ERROR) << msg; |
| } |
| |
| } // namespace |
| |
| DlcService::DlcService( |
| unique_ptr<org::chromium::ImageLoaderInterfaceProxyInterface> |
| image_loader_proxy, |
| unique_ptr<org::chromium::UpdateEngineInterfaceProxyInterface> |
| update_engine_proxy, |
| unique_ptr<BootSlot> boot_slot, |
| const FilePath& manifest_dir, |
| const FilePath& content_dir) |
| : image_loader_proxy_(std::move(image_loader_proxy)), |
| update_engine_proxy_(std::move(update_engine_proxy)), |
| boot_slot_(std::move(boot_slot)), |
| manifest_dir_(manifest_dir), |
| content_dir_(content_dir), |
| scheduled_period_ue_check_id_(MessageLoop::kTaskIdNull), |
| weak_ptr_factory_(this) { |
| // Get current boot slot. |
| string boot_disk_name; |
| int num_slots = -1; |
| int current_boot_slot = -1; |
| if (!boot_slot_->GetCurrentSlot(&boot_disk_name, &num_slots, |
| ¤t_boot_slot)) |
| LOG(FATAL) << "Can not get current boot slot."; |
| |
| current_boot_slot_name_ = current_boot_slot == 0 ? imageloader::kSlotNameA |
| : imageloader::kSlotNameB; |
| |
| // Register D-Bus signal callbacks. |
| update_engine_proxy_->RegisterStatusUpdateAdvancedSignalHandler( |
| base::Bind(&DlcService::OnStatusUpdateAdvancedSignal, |
| weak_ptr_factory_.GetWeakPtr()), |
| base::Bind(&DlcService::OnStatusUpdateAdvancedSignalConnected, |
| weak_ptr_factory_.GetWeakPtr())); |
| |
| // Initalize installed DLC modules. |
| for (auto installed_dlc_id : utils::ScanDirectory(content_dir_)) |
| installed_dlc_modules_[installed_dlc_id]; |
| |
| // Initialize supported DLC modules. |
| supported_dlc_modules_ = utils::ScanDirectory(manifest_dir_); |
| } |
| |
| DlcService::~DlcService() { |
| if (scheduled_period_ue_check_id_ != MessageLoop::kTaskIdNull && |
| !brillo::MessageLoop::current()->CancelTask( |
| scheduled_period_ue_check_id_)) |
| LOG(ERROR) |
| << "Failed to cancel delayed update_engine check during cleanup."; |
| } |
| |
| void DlcService::LoadDlcModuleImages() { |
| // Load all installed DLC modules. |
| for (auto installed_dlc_module_itr = installed_dlc_modules_.begin(); |
| installed_dlc_module_itr != installed_dlc_modules_.end(); |
| /* Don't increment here */) { |
| const string& installed_dlc_module_id = installed_dlc_module_itr->first; |
| string& installed_dlc_module_root = installed_dlc_module_itr->second; |
| |
| if (base::PathExists(FilePath(installed_dlc_module_root))) { |
| ++installed_dlc_module_itr; |
| continue; |
| } |
| |
| string mount_point; |
| if (!MountDlc(installed_dlc_module_id, &mount_point, nullptr)) { |
| LOG(ERROR) << "Failed to mount DLC module during load: " |
| << installed_dlc_module_id; |
| if (!DeleteDlc(installed_dlc_module_id, nullptr)) { |
| LOG(ERROR) << "Failed to delete an unmountable DLC module: " |
| << installed_dlc_module_id; |
| } |
| installed_dlc_modules_.erase(installed_dlc_module_itr++); |
| } else { |
| installed_dlc_module_root = |
| utils::GetDlcRootInModulePath(FilePath(mount_point)).value(); |
| ++installed_dlc_module_itr; |
| } |
| } |
| } |
| |
| bool DlcService::Install(const DlcModuleList& dlc_module_list_in, |
| brillo::ErrorPtr* err) { |
| if (dlc_module_list_in.dlc_module_infos().empty()) { |
| LogAndSetError(err, kErrorInvalidDlc, |
| "Must provide at least one DLC to install"); |
| return false; |
| } |
| |
| // Pick up DLC(s) from |DlcModuleList| passed in which have no roots. |
| DlcRootMap unique_dlcs = utils::ToDlcRootMap( |
| dlc_module_list_in, |
| [](DlcModuleInfo dlc) { return dlc.dlc_root().empty(); }); |
| |
| // Check that no duplicate DLC(s) were passed in. |
| if (unique_dlcs.size() != dlc_module_list_in.dlc_module_infos_size()) { |
| // Note: nice to have for log which was duplicate, but not necessary ATM. |
| LogAndSetError(err, kErrorInvalidDlc, |
| "Must not pass in duplicate DLC(s) to install"); |
| return false; |
| } |
| |
| LoadDlcModuleImages(); |
| |
| // Go through already installed DLC(s) and set the roots if found. |
| for (const auto& unique_dlc : unique_dlcs) { |
| const string& id = unique_dlc.first; |
| if (installed_dlc_modules_.find(id) != installed_dlc_modules_.end()) |
| unique_dlcs[id] = installed_dlc_modules_[id]; |
| } |
| |
| // This is the entire unique DLC(s) asked to be installed. |
| DlcModuleList unique_dlc_module_list = |
| utils::ToDlcModuleList(unique_dlcs, [](DlcId, DlcRoot) { return true; }); |
| // This is the unique DLC(s) that actually need to be installed. |
| DlcModuleList unique_dlc_module_list_to_install = utils::ToDlcModuleList( |
| unique_dlcs, [](DlcId, DlcRoot root) { return root.empty(); }); |
| // Copy over the Omaha URL. |
| unique_dlc_module_list_to_install.set_omaha_url( |
| dlc_module_list_in.omaha_url()); |
| |
| // Check if there is nothing to install. |
| if (unique_dlc_module_list_to_install.dlc_module_infos_size() == 0) { |
| InstallStatus install_status = utils::CreateInstallStatus( |
| Status::COMPLETED, kErrorNone, unique_dlc_module_list, 1.); |
| SendOnInstallStatusSignal(install_status); |
| return true; |
| } |
| |
| // If an install is already in progress, dlcservice is busy. |
| if (IsInstalling()) { |
| LogAndSetError(err, kErrorBusy, "Another install is already in progress."); |
| return false; |
| } |
| |
| Operation update_engine_operation; |
| if (!GetUpdateEngineStatus(&update_engine_operation)) { |
| LogAndSetError(err, kErrorInternal, |
| "Failed to get the status of Update Engine."); |
| return false; |
| } |
| switch (update_engine_operation) { |
| case update_engine::UPDATED_NEED_REBOOT: |
| LogAndSetError(err, kErrorNeedReboot, |
| "Update Engine applied update, device needs a reboot."); |
| return false; |
| case update_engine::IDLE: |
| break; |
| default: |
| LogAndSetError(err, kErrorBusy, |
| "Update Engine is performing operations."); |
| return false; |
| } |
| |
| // Note: this holds the list of directories that were created and need to be |
| // freed in case an error happens. |
| vector<unique_ptr<ScopedTempDir>> scoped_paths; |
| |
| for (const DlcModuleInfo& dlc_module : |
| unique_dlc_module_list_to_install.dlc_module_infos()) { |
| FilePath path; |
| const string& id = dlc_module.dlc_id(); |
| auto scoped_path = std::make_unique<ScopedTempDir>(); |
| |
| if (!CreateDlc(id, &path, err)) |
| return false; |
| |
| if (!scoped_path->Set(path)) { |
| LOG(ERROR) << "Failed when scoping path during install: " << path.value(); |
| return false; |
| } |
| |
| scoped_paths.emplace_back(std::move(scoped_path)); |
| } |
| |
| // Invokes update_engine to install the DLC module. |
| if (!update_engine_proxy_->AttemptInstall(unique_dlc_module_list_to_install, |
| nullptr)) { |
| // TODO(kimjae): need update engine to propagate correct error message by |
| // passing in |ErrorPtr| and being set within update engine, current default |
| // is to indicate that update engine is updating because there is no way an |
| // install should have taken place if not through dlcservice. (could also be |
| // the case that an update applied between the time of the last status check |
| // above, but just return |kErrorBusy| because the next time around if an |
| // update has been applied and is in a reboot needed state, it will indicate |
| // correctly then). |
| LogAndSetError(err, kErrorBusy, |
| "Update Engine failed to schedule install operations."); |
| return false; |
| } |
| |
| dlc_modules_being_installed_ = unique_dlc_module_list; |
| // Note: Do NOT add to installed indication. Let |
| // |OnStatusUpdateAdvancedSignal()| handle since that's truly when the DLC(s) |
| // are installed. |
| |
| // Safely take ownership of scoped paths for them not to be freed. |
| for (const auto& scoped_path : scoped_paths) |
| scoped_path->Take(); |
| |
| // Schedule a update_engine check during an install to verify that |
| // update_engine is installing the DLC(s). |
| SchedulePeriodicInstallCheck(true); |
| |
| return true; |
| } |
| |
| bool DlcService::Uninstall(const string& id_in, brillo::ErrorPtr* err) { |
| LoadDlcModuleImages(); |
| if (installed_dlc_modules_.find(id_in) == installed_dlc_modules_.end()) { |
| LOG(INFO) << "Uninstalling DLC id that's not installed: " << id_in; |
| return true; |
| } |
| |
| Operation update_engine_operation; |
| if (!GetUpdateEngineStatus(&update_engine_operation)) { |
| LogAndSetError(err, kErrorInternal, |
| "Failed to get the status of Update Engine"); |
| return false; |
| } |
| switch (update_engine_operation) { |
| case update_engine::IDLE: |
| case update_engine::UPDATED_NEED_REBOOT: |
| break; |
| default: |
| LogAndSetError(err, kErrorBusy, "Install or update is in progress."); |
| return false; |
| } |
| |
| // This means update_engine was restarted and requires cleanup of DLC(s) that |
| // were previously being thought to have been being installed. |
| if (IsInstalling()) |
| SendFailedSignalAndCleanup(); |
| |
| if (!UnmountDlc(id_in, err)) |
| return false; |
| |
| if (!DeleteDlc(id_in, err)) |
| return false; |
| |
| installed_dlc_modules_.erase(id_in); |
| LOG(INFO) << "Uninstalled DLC:" << id_in; |
| return true; |
| } |
| |
| bool DlcService::GetInstalled(DlcModuleList* dlc_module_list_out, |
| brillo::ErrorPtr* err) { |
| LoadDlcModuleImages(); |
| *dlc_module_list_out = utils::ToDlcModuleList( |
| installed_dlc_modules_, [](DlcId, DlcRoot) { return true; }); |
| return true; |
| } |
| |
| void DlcService::SendFailedSignalAndCleanup() { |
| SendOnInstallStatusSignal( |
| utils::CreateInstallStatus(Status::FAILED, kErrorInternal, {}, 0.)); |
| for (const auto& dlc_module : |
| dlc_modules_being_installed_.dlc_module_infos()) { |
| const string& dlc_id = dlc_module.dlc_id(); |
| if (!DeleteDlc(dlc_id, nullptr)) |
| LOG(ERROR) << "Failed to delete DLC(" << dlc_id << ") during cleanup."; |
| } |
| dlc_modules_being_installed_.clear_dlc_module_infos(); |
| } |
| |
| bool DlcService::IsInstalling() { |
| return !dlc_modules_being_installed_.dlc_module_infos().empty(); |
| } |
| |
| void DlcService::PeriodicInstallCheck() { |
| if (scheduled_period_ue_check_id_ == MessageLoop::kTaskIdNull) { |
| LOG(ERROR) << "Should not have been called unless scheduled."; |
| return; |
| } |
| |
| scheduled_period_ue_check_id_ = MessageLoop::kTaskIdNull; |
| |
| if (!IsInstalling()) { |
| LOG(ERROR) << "Should not have to check update_engine status while not " |
| "performing an install."; |
| return; |
| } |
| |
| Operation update_engine_operation; |
| if (!GetUpdateEngineStatus(&update_engine_operation)) { |
| LOG(ERROR) |
| << "Failed to get the status of update_engine, it is most likely down."; |
| SendFailedSignalAndCleanup(); |
| return; |
| } |
| switch (update_engine_operation) { |
| case update_engine::UPDATED_NEED_REBOOT: |
| LOG(ERROR) << "Thought to be installing DLC(s), but update_engine is not " |
| "installing and actually performed an update."; |
| SendFailedSignalAndCleanup(); |
| break; |
| case update_engine::IDLE: |
| if (scheduled_period_ue_check_retry_) { |
| LOG(INFO) << "Going to retry periodic check to check install signal."; |
| SchedulePeriodicInstallCheck(false); |
| return; |
| } |
| SendFailedSignalAndCleanup(); |
| break; |
| default: |
| SchedulePeriodicInstallCheck(true); |
| } |
| } |
| |
| void DlcService::SchedulePeriodicInstallCheck(bool retry) { |
| scheduled_period_ue_check_id_ = |
| brillo::MessageLoop::current()->PostDelayedTask( |
| FROM_HERE, |
| base::Bind(&DlcService::PeriodicInstallCheck, |
| weak_ptr_factory_.GetWeakPtr()), |
| base::TimeDelta::FromSeconds(kUECheckTimeout)); |
| scheduled_period_ue_check_retry_ = retry; |
| } |
| |
| bool DlcService::HandleStatusResult(const StatusResult& status_result) { |
| // If we are not installing any DLC(s), no need to even handle status result. |
| if (!IsInstalling()) |
| return false; |
| |
| // When a signal is received from update_engine, it is more efficient to |
| // cancel the periodic check that's scheduled by re-posting a delayed task |
| // after cancelling the currently set periodic check. If the cancelling of the |
| // periodic check fails, let it run as it will be rescheduled correctly within |
| // the periodic check itself again. |
| if (!brillo::MessageLoop::current()->CancelTask( |
| scheduled_period_ue_check_id_)) { |
| LOG(ERROR) << "Failed to cancel delayed update_engine check when signal " |
| "was received from update_engine, so letting it run."; |
| } else { |
| SchedulePeriodicInstallCheck(true); |
| } |
| |
| Operation update_engine_operation = status_result.current_operation(); |
| |
| if (update_engine_operation == Operation::REPORTING_ERROR_EVENT) { |
| LOG(ERROR) << "Signal from update_engine indicates reporting failure."; |
| SendFailedSignalAndCleanup(); |
| return false; |
| } |
| |
| // This situation is reached if update_engine crashes during an install and |
| // dlcservice still believes that it is waiting for an install to complete. |
| // TODO(kimjae): Need to handle checking preiodically if an install is started |
| // and dlcservice hasn't gotten an install progress in a certain period of |
| // time, try to explicitly check update_engine's status and act accordingly. |
| if (!status_result.is_install()) { |
| int last_attempt_error; |
| update_engine_proxy_->GetLastAttemptError(&last_attempt_error, nullptr); |
| LOG(ERROR) << "Signal from update_engine indicates non-install, so install " |
| << " failed and update_engine error code is: " |
| << "(" << last_attempt_error << ")"; |
| SendFailedSignalAndCleanup(); |
| return false; |
| } |
| |
| switch (update_engine_operation) { |
| case Operation::IDLE: |
| LOG(INFO) |
| << "Signal from update_engine, proceeding to complete installation."; |
| return true; |
| // Only when update_engine's |Operation::DOWNLOADING| should dlcservice send |
| // a signal out for |InstallStatus| for |Status::RUNNING|. Majority of the |
| // install process for DLC(s) is during |Operation::DOWNLOADING|, this also |
| // means that only a single growth from 0.0 to 1.0 for progress reporting |
| // will happen. |
| case Operation::DOWNLOADING: |
| SendOnInstallStatusSignal(utils::CreateInstallStatus( |
| Status::RUNNING, kErrorNone, {}, status_result.progress())); |
| FALLTHROUGH; |
| default: |
| return false; |
| } |
| } |
| |
| bool DlcService::CreateDlc(const string& id, |
| FilePath* path, |
| brillo::ErrorPtr* err) { |
| path->clear(); |
| if (supported_dlc_modules_.find(id) == supported_dlc_modules_.end()) { |
| LogAndSetError(err, kErrorInvalidDlc, |
| "The DLC ID provided is not supported."); |
| return false; |
| } |
| |
| const string& package = ScanDlcModulePackage(id); |
| FilePath module_path = utils::GetDlcModulePath(content_dir_, id); |
| FilePath module_package_path = |
| utils::GetDlcModulePackagePath(content_dir_, id, package); |
| |
| if (base::PathExists(module_path)) { |
| LogAndSetError(err, kErrorInternal, |
| "The DLC module is installed or duplicate."); |
| return false; |
| } |
| // Create the DLC ID directory with correct permissions. |
| if (!CreateDirWithDlcPermissions(module_path)) { |
| LogAndSetError(err, kErrorInternal, "Failed to create DLC ID directory"); |
| return false; |
| } |
| // Create the DLC package directory with correct permissions. |
| if (!CreateDirWithDlcPermissions(module_package_path)) { |
| LogAndSetError(err, kErrorInternal, |
| "Failed to create DLC ID package directory"); |
| return false; |
| } |
| |
| // Creates DLC module storage. |
| // TODO(xiaochu): Manifest currently returns a signed integer, which means it |
| // will likely fail for modules >= 2 GiB in size. https://crbug.com/904539 |
| imageloader::Manifest manifest; |
| if (!dlcservice::utils::GetDlcManifest(manifest_dir_, id, package, |
| &manifest)) { |
| LogAndSetError(err, kErrorInternal, "Failed to get DLC module manifest."); |
| return false; |
| } |
| int64_t image_size = manifest.preallocated_size(); |
| if (image_size <= 0) { |
| LogAndSetError(err, kErrorInternal, |
| "Preallocated size in manifest is illegal."); |
| return false; |
| } |
| |
| // Creates image A. |
| FilePath image_a_path = |
| utils::GetDlcModuleImagePath(content_dir_, id, package, 0); |
| if (!CreateImageFile(image_a_path, image_size)) { |
| LogAndSetError(err, kErrorInternal, |
| "Failed to create slot A DLC image file"); |
| return false; |
| } |
| |
| // Creates image B. |
| FilePath image_b_path = |
| utils::GetDlcModuleImagePath(content_dir_, id, package, 1); |
| if (!CreateImageFile(image_b_path, image_size)) { |
| LogAndSetError(err, kErrorInternal, "Failed to create slot B image file"); |
| return false; |
| } |
| |
| *path = module_path; |
| return true; |
| } |
| |
| bool DlcService::DeleteDlc(const std::string& id, brillo::ErrorPtr* err) { |
| FilePath dlc_module_path = utils::GetDlcModulePath(content_dir_, id); |
| if (!DeleteFile(dlc_module_path, true)) { |
| LogAndSetError(err, kErrorInternal, |
| "DLC image folder could not be deleted."); |
| return false; |
| } |
| return true; |
| } |
| |
| bool DlcService::MountDlc(const string& id, |
| string* mount_point, |
| brillo::ErrorPtr* err) { |
| if (!image_loader_proxy_->LoadDlcImage(id, ScanDlcModulePackage(id), |
| current_boot_slot_name_, mount_point, |
| nullptr)) { |
| LogAndSetError(err, kErrorInternal, "Imageloader is not available."); |
| return false; |
| } |
| if (mount_point->empty()) { |
| LogAndSetError(err, kErrorInternal, "Imageloader LoadDlcImage() failed."); |
| return false; |
| } |
| return true; |
| } |
| |
| bool DlcService::UnmountDlc(const string& id, brillo::ErrorPtr* err) { |
| bool success = false; |
| if (!image_loader_proxy_->UnloadDlcImage(id, ScanDlcModulePackage(id), |
| &success, nullptr)) { |
| LogAndSetError(err, kErrorInternal, "Imageloader is not available."); |
| return false; |
| } |
| if (!success) { |
| LogAndSetError(err, kErrorInternal, "Imageloader UnloadDlcImage failed."); |
| return false; |
| } |
| return true; |
| } |
| |
| string DlcService::ScanDlcModulePackage(const string& id) { |
| return *(utils::ScanDirectory(manifest_dir_.Append(id)).begin()); |
| } |
| |
| bool DlcService::GetUpdateEngineStatus(Operation* operation) { |
| StatusResult status_result; |
| if (!update_engine_proxy_->GetStatusAdvanced(&status_result, nullptr)) { |
| return false; |
| } |
| *operation = status_result.current_operation(); |
| return true; |
| } |
| |
| void DlcService::AddObserver(DlcService::Observer* observer) { |
| observers_.push_back(observer); |
| } |
| |
| void DlcService::SendOnInstallStatusSignal( |
| const InstallStatus& install_status) { |
| for (const auto& observer : observers_) { |
| observer->SendInstallStatus(install_status); |
| } |
| } |
| |
| void DlcService::OnStatusUpdateAdvancedSignal( |
| const StatusResult& status_result) { |
| if (!HandleStatusResult(status_result)) |
| return; |
| |
| // At this point, update_engine finished installation of the requested DLC(s). |
| DlcModuleList dlc_module_list, dlc_module_list_post_mount; |
| dlc_module_list.CopyFrom(dlc_modules_being_installed_); |
| dlc_module_list_post_mount.CopyFrom(dlc_modules_being_installed_); |
| dlc_modules_being_installed_.clear_dlc_module_infos(); |
| |
| // Keep track of the cleanups for DLC images. |
| utils::ScopedCleanups<Callback<void()>> scoped_cleanups; |
| for (const DlcModuleInfo& dlc_module : dlc_module_list.dlc_module_infos()) { |
| // Don't cleanup for already mounted. |
| if (!dlc_module.dlc_root().empty()) |
| continue; |
| const string& dlc_id = dlc_module.dlc_id(); |
| auto cleanup = base::Bind( |
| [](Callback<bool()> unmounter, Callback<bool()> deleter) { |
| unmounter.Run(); |
| deleter.Run(); |
| }, |
| base::Bind(&DlcService::UnmountDlc, base::Unretained(this), dlc_id, |
| nullptr), |
| base::Bind(&DlcService::DeleteDlc, base::Unretained(this), dlc_id, |
| nullptr)); |
| scoped_cleanups.Insert(cleanup); |
| } |
| |
| // Mount the installed DLC module images not already mounted. |
| for (auto& dlc_module : |
| *dlc_module_list_post_mount.mutable_dlc_module_infos()) { |
| // Don't remount already mounted. |
| if (!dlc_module.dlc_root().empty()) |
| continue; |
| const string& dlc_module_id = dlc_module.dlc_id(); |
| string mount_point; |
| if (!MountDlc(dlc_module_id, &mount_point, nullptr)) { |
| InstallStatus install_status = utils::CreateInstallStatus( |
| Status::FAILED, kErrorInternal, dlc_module_list, 0.); |
| SendOnInstallStatusSignal(install_status); |
| return; |
| } |
| dlc_module.set_dlc_root( |
| utils::GetDlcRootInModulePath(FilePath(mount_point)).value()); |
| } |
| |
| // Don't unmount+delete the images+directories as all successfully installed. |
| scoped_cleanups.Cancel(); |
| |
| // Install was a success so keep track. |
| for (const DlcModuleInfo& installed_dlc_module : |
| dlc_module_list_post_mount.dlc_module_infos()) |
| installed_dlc_modules_.emplace(installed_dlc_module.dlc_id(), |
| installed_dlc_module.dlc_root()); |
| |
| InstallStatus install_status = utils::CreateInstallStatus( |
| Status::COMPLETED, kErrorNone, dlc_module_list_post_mount, 1.); |
| SendOnInstallStatusSignal(install_status); |
| } |
| |
| void DlcService::OnStatusUpdateAdvancedSignalConnected( |
| const string& interface_name, const string& signal_name, bool success) { |
| if (!success) { |
| LOG(ERROR) << "Failed to connect to update_engine's StatusUpdate signal."; |
| } |
| } |
| |
| } // namespace dlcservice |