Amin Hassani | a69f32e | 2020-03-30 15:20:42 -0700 | [diff] [blame] | 1 | // Copyright 2020 The Chromium OS Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #include "dlcservice/dlc.h" |
| 6 | |
| 7 | #include <algorithm> |
| 8 | #include <cinttypes> |
Amin Hassani | 40c0f11 | 2020-04-15 09:55:00 -0700 | [diff] [blame] | 9 | #include <memory> |
Amin Hassani | a69f32e | 2020-03-30 15:20:42 -0700 | [diff] [blame] | 10 | #include <utility> |
| 11 | #include <vector> |
| 12 | |
Qijiang Fan | 713061e | 2021-03-08 15:45:12 +0900 | [diff] [blame] | 13 | #include <base/check.h> |
Qijiang Fan | 886c469 | 2021-02-19 11:54:10 +0900 | [diff] [blame] | 14 | #include <base/notreached.h> |
Amin Hassani | a69f32e | 2020-03-30 15:20:42 -0700 | [diff] [blame] | 15 | #include <base/strings/stringprintf.h> |
Jae Hoon Kim | 760a694 | 2020-04-15 10:01:02 -0700 | [diff] [blame] | 16 | #include <base/strings/string_number_conversions.h> |
Amin Hassani | a69f32e | 2020-03-30 15:20:42 -0700 | [diff] [blame] | 17 | #include <base/strings/string_util.h> |
| 18 | #include <brillo/errors/error.h> |
| 19 | #include <chromeos/dbus/service_constants.h> |
| 20 | #include <dbus/dlcservice/dbus-constants.h> |
| 21 | |
| 22 | #include "dlcservice/error.h" |
Jae Hoon Kim | 9a27d15 | 2020-04-10 12:50:14 -0700 | [diff] [blame] | 23 | #include "dlcservice/prefs.h" |
Amin Hassani | a69f32e | 2020-03-30 15:20:42 -0700 | [diff] [blame] | 24 | #include "dlcservice/system_state.h" |
| 25 | #include "dlcservice/utils.h" |
| 26 | |
Amin Hassani | 4ca7e1c | 2020-04-29 13:01:33 -0700 | [diff] [blame] | 27 | using base::FilePath; |
| 28 | using brillo::ErrorPtr; |
Amin Hassani | a69f32e | 2020-03-30 15:20:42 -0700 | [diff] [blame] | 29 | using std::string; |
| 30 | using std::vector; |
| 31 | |
| 32 | namespace dlcservice { |
Jae Hoon Kim | 0c2b6cd | 2020-04-30 13:23:16 -0700 | [diff] [blame] | 33 | |
| 34 | // static |
| 35 | vector<FilePath> DlcBase::GetPathsToDelete(const DlcId& id) { |
| 36 | const auto* system_state = SystemState::Get(); |
| 37 | return {JoinPaths(system_state->content_dir(), id), |
| 38 | JoinPaths(system_state->dlc_prefs_dir(), id)}; |
| 39 | } |
| 40 | |
Andrew | a9c9e10 | 2020-05-05 12:27:35 -0700 | [diff] [blame] | 41 | // TODO(ahassani): Instead of initialize function, create a factory method so |
Amin Hassani | a69f32e | 2020-03-30 15:20:42 -0700 | [diff] [blame] | 42 | // we can develop different types of DLC classes. |
| 43 | bool DlcBase::Initialize() { |
Amin Hassani | fe63fc2 | 2020-04-07 11:34:34 -0700 | [diff] [blame] | 44 | const auto* system_state = SystemState::Get(); |
| 45 | const auto& manifest_dir = system_state->manifest_dir(); |
Amin Hassani | a69f32e | 2020-03-30 15:20:42 -0700 | [diff] [blame] | 46 | package_ = *ScanDirectory(manifest_dir.Append(id_)).begin(); |
Amin Hassani | fe63fc2 | 2020-04-07 11:34:34 -0700 | [diff] [blame] | 47 | if (!GetDlcManifest(system_state->manifest_dir(), id_, package_, |
Amin Hassani | a69f32e | 2020-03-30 15:20:42 -0700 | [diff] [blame] | 48 | &manifest_)) { |
| 49 | // Failing to read the manifest will be considered a blocker. |
| 50 | LOG(ERROR) << "Failed to read the manifest of DLC " << id_; |
| 51 | return false; |
| 52 | } |
| 53 | |
Amin Hassani | fe63fc2 | 2020-04-07 11:34:34 -0700 | [diff] [blame] | 54 | const auto& content_dir = system_state->content_dir(); |
Jae Hoon Kim | dcadbd4 | 2020-06-25 09:21:03 -0700 | [diff] [blame] | 55 | content_id_path_ = JoinPaths(content_dir, id_); |
| 56 | content_package_path_ = JoinPaths(content_id_path_, package_); |
| 57 | prefs_path_ = JoinPaths(system_state->dlc_prefs_dir(), id_); |
| 58 | prefs_package_path_ = JoinPaths(prefs_path_, package_); |
Jae Hoon Kim | 0d4ff62 | 2020-05-14 14:47:40 -0700 | [diff] [blame] | 59 | preloaded_image_path_ = JoinPaths(system_state->preloaded_content_dir(), id_, |
| 60 | package_, kDlcImageFileName); |
Amin Hassani | 40c0f11 | 2020-04-15 09:55:00 -0700 | [diff] [blame] | 61 | ref_count_ = RefCountInterface::Create(manifest_.used_by(), prefs_path_); |
Amin Hassani | a69f32e | 2020-03-30 15:20:42 -0700 | [diff] [blame] | 62 | |
Amin Hassani | f27aac0 | 2020-04-23 21:56:26 -0700 | [diff] [blame] | 63 | state_.set_state(DlcState::NOT_INSTALLED); |
Amin Hassani | 78a5ec8 | 2020-05-19 09:47:49 -0700 | [diff] [blame] | 64 | state_.set_id(id_); |
| 65 | state_.set_progress(0); |
| 66 | state_.set_last_error_code(kErrorNone); |
Amin Hassani | a69f32e | 2020-03-30 15:20:42 -0700 | [diff] [blame] | 67 | |
Jae Hoon Kim | 4630420 | 2020-07-17 15:15:45 -0700 | [diff] [blame] | 68 | if (manifest_.mount_file_required()) { |
| 69 | if (!Prefs(prefs_package_path_).Delete(kDlcRootMount)) |
| 70 | LOG(ERROR) |
| 71 | << "Failed to delete indirect root mount file during Initialization: " |
| 72 | << JoinPaths(prefs_package_path_, kDlcRootMount); |
| 73 | } |
Vyshu Khota | 32388f9 | 2020-11-10 09:13:29 -0800 | [diff] [blame] | 74 | state_.set_is_verified( |
| 75 | Prefs(*this, system_state->active_boot_slot()).Exists(kDlcPrefVerified)); |
Amin Hassani | a69f32e | 2020-03-30 15:20:42 -0700 | [diff] [blame] | 76 | return true; |
| 77 | } |
| 78 | |
Amin Hassani | 4ca7e1c | 2020-04-29 13:01:33 -0700 | [diff] [blame] | 79 | const DlcId& DlcBase::GetId() const { |
Jae Hoon Kim | e567b9e | 2020-04-08 14:43:59 -0700 | [diff] [blame] | 80 | return id_; |
| 81 | } |
| 82 | |
Amin Hassani | d5fc8b2 | 2020-04-29 12:44:52 -0700 | [diff] [blame] | 83 | const std::string& DlcBase::GetName() const { |
| 84 | return manifest_.name(); |
| 85 | } |
| 86 | |
| 87 | const std::string& DlcBase::GetDescription() const { |
| 88 | return manifest_.description(); |
| 89 | } |
| 90 | |
Amin Hassani | a69f32e | 2020-03-30 15:20:42 -0700 | [diff] [blame] | 91 | DlcState DlcBase::GetState() const { |
| 92 | return state_; |
| 93 | } |
| 94 | |
| 95 | bool DlcBase::IsInstalling() const { |
| 96 | return state_.state() == DlcState::INSTALLING; |
| 97 | } |
Jae Hoon Kim | 0d057fb | 2020-04-13 17:44:38 -0700 | [diff] [blame] | 98 | |
Amin Hassani | a69f32e | 2020-03-30 15:20:42 -0700 | [diff] [blame] | 99 | bool DlcBase::IsInstalled() const { |
| 100 | return state_.state() == DlcState::INSTALLED; |
| 101 | } |
| 102 | |
Amin Hassani | f27aac0 | 2020-04-23 21:56:26 -0700 | [diff] [blame] | 103 | bool DlcBase::IsVerified() const { |
Vyshu Khota | 32388f9 | 2020-11-10 09:13:29 -0800 | [diff] [blame] | 104 | return state_.is_verified(); |
Jae Hoon Kim | 0d057fb | 2020-04-13 17:44:38 -0700 | [diff] [blame] | 105 | } |
| 106 | |
Amin Hassani | d5fc8b2 | 2020-04-29 12:44:52 -0700 | [diff] [blame] | 107 | bool DlcBase::HasContent() const { |
| 108 | for (const auto& path : |
| 109 | {GetImagePath(BootSlot::Slot::A), GetImagePath(BootSlot::Slot::B)}) { |
| 110 | if (base::PathExists(path)) |
| 111 | return true; |
| 112 | } |
| 113 | return false; |
| 114 | } |
| 115 | |
| 116 | uint64_t DlcBase::GetUsedBytesOnDisk() const { |
| 117 | uint64_t total_size = 0; |
| 118 | for (const auto& path : |
| 119 | {GetImagePath(BootSlot::Slot::A), GetImagePath(BootSlot::Slot::B)}) { |
| 120 | if (!base::PathExists(path)) |
| 121 | continue; |
| 122 | int64_t size = 0; |
| 123 | if (!base::GetFileSize(path, &size)) { |
| 124 | LOG(WARNING) << "Failed to get file size for path: " << path.value(); |
| 125 | } |
| 126 | total_size += size; |
| 127 | } |
| 128 | return total_size; |
| 129 | } |
| 130 | |
Amin Hassani | a69f32e | 2020-03-30 15:20:42 -0700 | [diff] [blame] | 131 | bool DlcBase::IsPreloadAllowed() const { |
Jae Hoon Kim | 9318f34 | 2020-09-10 14:38:13 -0700 | [diff] [blame] | 132 | return manifest_.preload_allowed() && |
| 133 | !SystemState::Get()->system_properties()->IsOfficialBuild(); |
Amin Hassani | a69f32e | 2020-03-30 15:20:42 -0700 | [diff] [blame] | 134 | } |
| 135 | |
| 136 | base::FilePath DlcBase::GetRoot() const { |
Jae Hoon Kim | 2c61e9e | 2020-04-07 15:03:35 -0700 | [diff] [blame] | 137 | if (mount_point_.empty()) |
| 138 | return {}; |
Jae Hoon Kim | e567b9e | 2020-04-08 14:43:59 -0700 | [diff] [blame] | 139 | return JoinPaths(mount_point_, kRootDirectoryInsideDlcModule); |
Amin Hassani | a69f32e | 2020-03-30 15:20:42 -0700 | [diff] [blame] | 140 | } |
| 141 | |
Amin Hassani | f27aac0 | 2020-04-23 21:56:26 -0700 | [diff] [blame] | 142 | bool DlcBase::InstallCompleted(ErrorPtr* err) { |
Amin Hassani | 6b7a9aa | 2020-05-29 14:23:47 -0700 | [diff] [blame] | 143 | if (!MarkVerified()) { |
Amin Hassani | 78a5ec8 | 2020-05-19 09:47:49 -0700 | [diff] [blame] | 144 | state_.set_last_error_code(kErrorInternal); |
Jae Hoon Kim | 9a27d15 | 2020-04-10 12:50:14 -0700 | [diff] [blame] | 145 | *err = Error::Create( |
Amin Hassani | 78a5ec8 | 2020-05-19 09:47:49 -0700 | [diff] [blame] | 146 | FROM_HERE, state_.last_error_code(), |
Amin Hassani | f27aac0 | 2020-04-23 21:56:26 -0700 | [diff] [blame] | 147 | base::StringPrintf("Failed to mark active DLC=%s as verified.", |
| 148 | id_.c_str())); |
| 149 | return false; |
| 150 | } |
Amin Hassani | f27aac0 | 2020-04-23 21:56:26 -0700 | [diff] [blame] | 151 | return true; |
| 152 | } |
| 153 | |
Amin Hassani | 78a5ec8 | 2020-05-19 09:47:49 -0700 | [diff] [blame] | 154 | bool DlcBase::UpdateCompleted(ErrorPtr* err) { |
Amin Hassani | f27aac0 | 2020-04-23 21:56:26 -0700 | [diff] [blame] | 155 | if (!Prefs(*this, SystemState::Get()->inactive_boot_slot()) |
| 156 | .Create(kDlcPrefVerified)) { |
| 157 | *err = Error::Create( |
Andrew | 9aa0644 | 2020-05-06 10:33:46 -0700 | [diff] [blame] | 158 | FROM_HERE, kErrorInternal, |
Amin Hassani | f27aac0 | 2020-04-23 21:56:26 -0700 | [diff] [blame] | 159 | base::StringPrintf("Failed to mark inactive DLC=%s as verified.", |
| 160 | id_.c_str())); |
Jae Hoon Kim | 9a27d15 | 2020-04-10 12:50:14 -0700 | [diff] [blame] | 161 | return false; |
| 162 | } |
| 163 | return true; |
| 164 | } |
| 165 | |
Amin Hassani | a69f32e | 2020-03-30 15:20:42 -0700 | [diff] [blame] | 166 | FilePath DlcBase::GetImagePath(BootSlot::Slot slot) const { |
Jae Hoon Kim | e567b9e | 2020-04-08 14:43:59 -0700 | [diff] [blame] | 167 | return JoinPaths(content_package_path_, BootSlot::ToString(slot), |
| 168 | kDlcImageFileName); |
Amin Hassani | a69f32e | 2020-03-30 15:20:42 -0700 | [diff] [blame] | 169 | } |
| 170 | |
Amin Hassani | c0867b6 | 2020-04-16 09:44:34 -0700 | [diff] [blame] | 171 | bool DlcBase::CreateDlc(ErrorPtr* err) { |
Amin Hassani | a69f32e | 2020-03-30 15:20:42 -0700 | [diff] [blame] | 172 | // Create content directories. |
Amin Hassani | c0867b6 | 2020-04-16 09:44:34 -0700 | [diff] [blame] | 173 | for (const auto& path : |
| 174 | {content_id_path_, content_package_path_, prefs_path_}) { |
Amin Hassani | a69f32e | 2020-03-30 15:20:42 -0700 | [diff] [blame] | 175 | if (!CreateDir(path)) { |
Andrew | 0a534ed | 2020-05-06 09:59:17 -0700 | [diff] [blame] | 176 | *err = Error::CreateInternal( |
| 177 | FROM_HERE, error::kFailedToCreateDirectory, |
Amin Hassani | a69f32e | 2020-03-30 15:20:42 -0700 | [diff] [blame] | 178 | base::StringPrintf("Failed to create directory %s for DLC=%s", |
| 179 | path.value().c_str(), id_.c_str())); |
Jae Hoon Kim | e9c30f7 | 2021-03-12 16:26:00 -0800 | [diff] [blame] | 180 | state_.set_last_error_code(Error::GetErrorCode(*err)); |
Amin Hassani | a69f32e | 2020-03-30 15:20:42 -0700 | [diff] [blame] | 181 | return false; |
| 182 | } |
| 183 | } |
| 184 | |
Amin Hassani | a69f32e | 2020-03-30 15:20:42 -0700 | [diff] [blame] | 185 | // Creates image A and B. |
| 186 | for (const auto& slot : {BootSlot::Slot::A, BootSlot::Slot::B}) { |
| 187 | FilePath image_path = GetImagePath(slot); |
Vyshu Khota | fe57408 | 2021-03-09 16:32:23 -0500 | [diff] [blame] | 188 | if (!CreateFile(image_path, manifest_.size())) { |
Amin Hassani | 78a5ec8 | 2020-05-19 09:47:49 -0700 | [diff] [blame] | 189 | state_.set_last_error_code(kErrorAllocation); |
Amin Hassani | a69f32e | 2020-03-30 15:20:42 -0700 | [diff] [blame] | 190 | *err = Error::Create( |
Amin Hassani | 78a5ec8 | 2020-05-19 09:47:49 -0700 | [diff] [blame] | 191 | FROM_HERE, state_.last_error_code(), |
Amin Hassani | a69f32e | 2020-03-30 15:20:42 -0700 | [diff] [blame] | 192 | base::StringPrintf("Failed to create image file %s for DLC=%s", |
| 193 | image_path.value().c_str(), id_.c_str())); |
| 194 | return false; |
Vyshu Khota | fe57408 | 2021-03-09 16:32:23 -0500 | [diff] [blame] | 195 | } else if (!ResizeFile(image_path, manifest_.preallocated_size())) { |
| 196 | LOG(WARNING) << "Unable to allocate up to preallocated size: " |
| 197 | << manifest_.preallocated_size() << " for DLC=" << id_; |
Amin Hassani | a69f32e | 2020-03-30 15:20:42 -0700 | [diff] [blame] | 198 | } |
| 199 | } |
| 200 | |
Amin Hassani | a69f32e | 2020-03-30 15:20:42 -0700 | [diff] [blame] | 201 | return true; |
| 202 | } |
| 203 | |
Amin Hassani | 4856777 | 2020-05-20 16:21:11 -0700 | [diff] [blame] | 204 | bool DlcBase::MakeReadyForUpdate() const { |
| 205 | // Deleting the inactive verified pref should always happen before anything |
| 206 | // else here otherwise if we failed to delete, on a reboot after an update, we |
| 207 | // might assume the image is verified, which is not. |
| 208 | if (!Prefs(*this, SystemState::Get()->inactive_boot_slot()) |
| 209 | .Delete(kDlcPrefVerified)) { |
| 210 | PLOG(ERROR) << "Failed to mark inactive DLC=" << id_ << " as not-verified."; |
| 211 | return false; |
Amin Hassani | a69f32e | 2020-03-30 15:20:42 -0700 | [diff] [blame] | 212 | } |
| 213 | |
Amin Hassani | 4856777 | 2020-05-20 16:21:11 -0700 | [diff] [blame] | 214 | if (!IsVerified()) { |
| 215 | return false; |
| 216 | } |
| 217 | |
| 218 | const FilePath& inactive_image_path = |
| 219 | GetImagePath(SystemState::Get()->inactive_boot_slot()); |
Vyshu Khota | fe57408 | 2021-03-09 16:32:23 -0500 | [diff] [blame] | 220 | if (!CreateFile(inactive_image_path, manifest_.size())) { |
Amin Hassani | 4856777 | 2020-05-20 16:21:11 -0700 | [diff] [blame] | 221 | LOG(ERROR) << "Failed to create inactive image " |
| 222 | << inactive_image_path.value() << " when making DLC=" << id_ |
| 223 | << " ready for update."; |
| 224 | return false; |
Vyshu Khota | fe57408 | 2021-03-09 16:32:23 -0500 | [diff] [blame] | 225 | } else if (!ResizeFile(inactive_image_path, manifest_.preallocated_size())) { |
| 226 | LOG(WARNING) << "Unable to allocate up to preallocated size: " |
| 227 | << manifest_.preallocated_size() << " when making DLC=" << id_ |
| 228 | << " ready for update."; |
Amin Hassani | a69f32e | 2020-03-30 15:20:42 -0700 | [diff] [blame] | 229 | } |
Vyshu Khota | fe57408 | 2021-03-09 16:32:23 -0500 | [diff] [blame] | 230 | |
Amin Hassani | a69f32e | 2020-03-30 15:20:42 -0700 | [diff] [blame] | 231 | return true; |
| 232 | } |
| 233 | |
Amin Hassani | 6b7a9aa | 2020-05-29 14:23:47 -0700 | [diff] [blame] | 234 | bool DlcBase::MarkVerified() { |
Vyshu Khota | 32388f9 | 2020-11-10 09:13:29 -0800 | [diff] [blame] | 235 | state_.set_is_verified(true); |
Amin Hassani | 6b7a9aa | 2020-05-29 14:23:47 -0700 | [diff] [blame] | 236 | return Prefs(*this, SystemState::Get()->active_boot_slot()) |
| 237 | .Create(kDlcPrefVerified); |
| 238 | } |
| 239 | |
| 240 | bool DlcBase::MarkUnverified() { |
Vyshu Khota | 32388f9 | 2020-11-10 09:13:29 -0800 | [diff] [blame] | 241 | state_.set_is_verified(false); |
Amin Hassani | 6b7a9aa | 2020-05-29 14:23:47 -0700 | [diff] [blame] | 242 | return Prefs(*this, SystemState::Get()->active_boot_slot()) |
| 243 | .Delete(kDlcPrefVerified); |
Amin Hassani | 28ed900 | 2020-05-28 12:37:01 -0700 | [diff] [blame] | 244 | } |
| 245 | |
Jae Hoon Kim | 8ab2649 | 2020-05-07 13:49:36 -0700 | [diff] [blame] | 246 | bool DlcBase::Verify() { |
| 247 | auto image_path = GetImagePath(SystemState::Get()->active_boot_slot()); |
| 248 | vector<uint8_t> image_sha256; |
Amin Hassani | f2efc5a | 2020-05-25 21:22:57 -0700 | [diff] [blame] | 249 | if (!HashFile(image_path, manifest_.size(), &image_sha256)) { |
Jae Hoon Kim | 8ab2649 | 2020-05-07 13:49:36 -0700 | [diff] [blame] | 250 | LOG(ERROR) << "Failed to hash image file: " << image_path.value(); |
| 251 | return false; |
| 252 | } |
Amin Hassani | f2efc5a | 2020-05-25 21:22:57 -0700 | [diff] [blame] | 253 | |
Jae Hoon Kim | 8ab2649 | 2020-05-07 13:49:36 -0700 | [diff] [blame] | 254 | const auto& manifest_image_sha256 = manifest_.image_sha256(); |
| 255 | if (image_sha256 != manifest_image_sha256) { |
| 256 | LOG(WARNING) << "Verification failed for image file: " << image_path.value() |
| 257 | << ". Expected: " |
| 258 | << base::HexEncode(manifest_image_sha256.data(), |
| 259 | manifest_image_sha256.size()) |
| 260 | << " Found: " |
| 261 | << base::HexEncode(image_sha256.data(), image_sha256.size()); |
| 262 | return false; |
| 263 | } |
Amin Hassani | 6b7a9aa | 2020-05-29 14:23:47 -0700 | [diff] [blame] | 264 | |
| 265 | if (!MarkVerified()) { |
| 266 | LOG(WARNING) << "Failed to mark the image as verified, but temporarily" |
| 267 | << " we assume the image is verified."; |
Jae Hoon Kim | 8ab2649 | 2020-05-07 13:49:36 -0700 | [diff] [blame] | 268 | } |
| 269 | return true; |
| 270 | } |
| 271 | |
Jae Hoon Kim | 0d4ff62 | 2020-05-14 14:47:40 -0700 | [diff] [blame] | 272 | bool DlcBase::PreloadedCopier(ErrorPtr* err) { |
Amin Hassani | 28ed900 | 2020-05-28 12:37:01 -0700 | [diff] [blame] | 273 | int64_t preloaded_image_size; |
| 274 | if (!base::GetFileSize(preloaded_image_path_, &preloaded_image_size)) { |
| 275 | auto err_str = base::StringPrintf("Failed to get preloaded DLC (%s) size.", |
| 276 | id_.c_str()); |
| 277 | *err = Error::Create(FROM_HERE, kErrorInternal, err_str); |
| 278 | return false; |
| 279 | } |
| 280 | if (preloaded_image_size != manifest_.size()) { |
| 281 | auto err_str = base::StringPrintf( |
| 282 | "Preloaded DLC (%s) is (%" PRId64 ") different than the size (%" PRId64 |
| 283 | ") in the manifest.", |
| 284 | id_.c_str(), preloaded_image_size, manifest_.size()); |
| 285 | *err = Error::Create(FROM_HERE, kErrorInternal, err_str); |
| 286 | return false; |
Amin Hassani | a69f32e | 2020-03-30 15:20:42 -0700 | [diff] [blame] | 287 | } |
| 288 | |
Amin Hassani | 28ed900 | 2020-05-28 12:37:01 -0700 | [diff] [blame] | 289 | // Before touching the image, we need to mark it as unverified. |
| 290 | MarkUnverified(); |
| 291 | |
| 292 | // TODO(kimjae): When preloaded images are place into unencrypted, this |
Amin Hassani | a69f32e | 2020-03-30 15:20:42 -0700 | [diff] [blame] | 293 | // operation can be a move. |
Amin Hassani | 28ed900 | 2020-05-28 12:37:01 -0700 | [diff] [blame] | 294 | FilePath image_path = GetImagePath(SystemState::Get()->active_boot_slot()); |
Amin Hassani | 0d3ab93 | 2020-04-26 17:35:04 -0700 | [diff] [blame] | 295 | vector<uint8_t> image_sha256; |
Amin Hassani | 28ed900 | 2020-05-28 12:37:01 -0700 | [diff] [blame] | 296 | if (!CopyAndHashFile(preloaded_image_path_, image_path, manifest_.size(), |
Amin Hassani | f2efc5a | 2020-05-25 21:22:57 -0700 | [diff] [blame] | 297 | &image_sha256)) { |
Jae Hoon Kim | 0d4ff62 | 2020-05-14 14:47:40 -0700 | [diff] [blame] | 298 | auto err_str = |
| 299 | base::StringPrintf("Failed to copy preload DLC (%s) into path %s", |
Amin Hassani | 28ed900 | 2020-05-28 12:37:01 -0700 | [diff] [blame] | 300 | id_.c_str(), image_path.value().c_str()); |
Jae Hoon Kim | 0d4ff62 | 2020-05-14 14:47:40 -0700 | [diff] [blame] | 301 | *err = Error::Create(FROM_HERE, kErrorInternal, err_str); |
Jae Hoon Kim | 760a694 | 2020-04-15 10:01:02 -0700 | [diff] [blame] | 302 | return false; |
| 303 | } |
| 304 | |
Jae Hoon Kim | 0d4ff62 | 2020-05-14 14:47:40 -0700 | [diff] [blame] | 305 | auto manifest_image_sha256 = manifest_.image_sha256(); |
| 306 | if (image_sha256 != manifest_image_sha256) { |
| 307 | auto err_str = base::StringPrintf( |
| 308 | "Image is corrupted or modified for DLC=%s. Expected: %s Found: %s", |
| 309 | id_.c_str(), |
| 310 | base::HexEncode(manifest_image_sha256.data(), |
| 311 | manifest_image_sha256.size()) |
| 312 | .c_str(), |
| 313 | base::HexEncode(image_sha256.data(), image_sha256.size()).c_str()); |
| 314 | *err = Error::Create(FROM_HERE, kErrorInternal, err_str); |
Jae Hoon Kim | 760a694 | 2020-04-15 10:01:02 -0700 | [diff] [blame] | 315 | return false; |
| 316 | } |
| 317 | |
Jae Hoon Kim | f49b6d7 | 2020-06-15 11:29:24 -0700 | [diff] [blame] | 318 | if (!MarkVerified()) |
| 319 | LOG(ERROR) << "Failed to mark the image verified for DLC=" << id_; |
Jae Hoon Kim | 0d057fb | 2020-04-13 17:44:38 -0700 | [diff] [blame] | 320 | |
Amin Hassani | a69f32e | 2020-03-30 15:20:42 -0700 | [diff] [blame] | 321 | return true; |
| 322 | } |
| 323 | |
Amin Hassani | 9a3f20c | 2020-05-25 16:38:33 -0700 | [diff] [blame] | 324 | bool DlcBase::Install(ErrorPtr* err) { |
Amin Hassani | a69f32e | 2020-03-30 15:20:42 -0700 | [diff] [blame] | 325 | switch (state_.state()) { |
Jae Hoon Kim | e9a5e13 | 2020-06-03 11:55:05 -0700 | [diff] [blame] | 326 | case DlcState::NOT_INSTALLED: { |
| 327 | bool active_image_existed = IsActiveImagePresent(); |
Amin Hassani | c0867b6 | 2020-04-16 09:44:34 -0700 | [diff] [blame] | 328 | // Always try to create the DLC files and directories to make sure they |
| 329 | // all exist before we start the install. |
| 330 | if (!CreateDlc(err)) { |
Andrew | bcc4bd8 | 2020-06-11 14:23:55 -0700 | [diff] [blame] | 331 | ErrorPtr tmp_err; |
| 332 | if (!CancelInstall(*err, &tmp_err)) |
| 333 | LOG(ERROR) << "Failed to cancel the install correctly."; |
Amin Hassani | a69f32e | 2020-03-30 15:20:42 -0700 | [diff] [blame] | 334 | return false; |
| 335 | } |
Jae Hoon Kim | 099c2f9 | 2020-07-24 10:06:38 -0700 | [diff] [blame] | 336 | // Only set the DLC installing after creation is successful to have finer |
| 337 | // control of state changes. |
| 338 | ChangeState(DlcState::INSTALLING); |
Amin Hassani | 9a3f20c | 2020-05-25 16:38:33 -0700 | [diff] [blame] | 339 | |
Jae Hoon Kim | f49b6d7 | 2020-06-15 11:29:24 -0700 | [diff] [blame] | 340 | // Finish the installation for verified images so they can be mounted. |
| 341 | if (IsVerified()) { |
| 342 | LOG(INFO) << "Installing already verified DLC=" << id_; |
| 343 | break; |
| 344 | } |
| 345 | |
| 346 | // Try verifying images that already existed before creation. If verified, |
| 347 | // finish the installation so they can be mounted. |
| 348 | if (active_image_existed && Verify()) { |
| 349 | LOG(INFO) << "Verified existing, but previously not verified DLC=" |
| 350 | << id_; |
| 351 | break; |
| 352 | } |
| 353 | |
| 354 | // Preload the DLC if possible. |
Amin Hassani | 9a3f20c | 2020-05-25 16:38:33 -0700 | [diff] [blame] | 355 | if (IsPreloadAllowed() && base::PathExists(preloaded_image_path_)) { |
Jae Hoon Kim | f49b6d7 | 2020-06-15 11:29:24 -0700 | [diff] [blame] | 356 | if (!PreloadedCopier(err)) { |
| 357 | LOG(ERROR) |
| 358 | << "Preloading failed, so assuming installation failed for DLC=" |
| 359 | << id_; |
Jae Hoon Kim | 13e6151 | 2020-06-26 16:36:10 -0700 | [diff] [blame] | 360 | ErrorPtr tmp_err; |
| 361 | if (!CancelInstall(*err, &tmp_err)) |
| 362 | LOG(ERROR) << "Failed to cancel the install from preloading."; |
Amin Hassani | 9a3f20c | 2020-05-25 16:38:33 -0700 | [diff] [blame] | 363 | return false; |
Amin Hassani | c0867b6 | 2020-04-16 09:44:34 -0700 | [diff] [blame] | 364 | } |
Jae Hoon Kim | f49b6d7 | 2020-06-15 11:29:24 -0700 | [diff] [blame] | 365 | LOG(INFO) << "Preloading DLC=" << id_; |
| 366 | break; |
Amin Hassani | c0867b6 | 2020-04-16 09:44:34 -0700 | [diff] [blame] | 367 | } |
Amin Hassani | 9a3f20c | 2020-05-25 16:38:33 -0700 | [diff] [blame] | 368 | |
Jae Hoon Kim | a9ce3f8 | 2020-07-24 11:39:21 -0700 | [diff] [blame] | 369 | // By now the image is not verified, so it needs to be installed |
| 370 | // through update_engine. So don't go any further. |
| 371 | return true; |
Jae Hoon Kim | e9a5e13 | 2020-06-03 11:55:05 -0700 | [diff] [blame] | 372 | } |
Amin Hassani | a69f32e | 2020-03-30 15:20:42 -0700 | [diff] [blame] | 373 | case DlcState::INSTALLING: |
Amin Hassani | 9a3f20c | 2020-05-25 16:38:33 -0700 | [diff] [blame] | 374 | // If the image is already in this state, nothing need to be done. It is |
| 375 | // already being installed. |
Andrew | 0a534ed | 2020-05-06 09:59:17 -0700 | [diff] [blame] | 376 | // Skip reporting this scenario to the metrics, since the Install call |
| 377 | // might be from the same client, and reporting this is not useful. |
Amin Hassani | 9a3f20c | 2020-05-25 16:38:33 -0700 | [diff] [blame] | 378 | return true; |
| 379 | case DlcState::INSTALLED: |
| 380 | // If the image is already installed, we need to finish the install so it |
| 381 | // gets mounted in case it has been unmounted externally. |
| 382 | break; |
Amin Hassani | a69f32e | 2020-03-30 15:20:42 -0700 | [diff] [blame] | 383 | default: |
| 384 | NOTREACHED(); |
| 385 | return false; |
| 386 | } |
Amin Hassani | 9a3f20c | 2020-05-25 16:38:33 -0700 | [diff] [blame] | 387 | |
| 388 | // Let's try to finish the installation. |
Andrew | 0a534ed | 2020-05-06 09:59:17 -0700 | [diff] [blame] | 389 | if (!FinishInstall(/*installed_by_ue=*/false, err)) { |
Amin Hassani | 9a3f20c | 2020-05-25 16:38:33 -0700 | [diff] [blame] | 390 | return false; |
| 391 | } |
| 392 | |
Jae Hoon Kim | 92e43e4 | 2020-06-12 15:26:30 -0700 | [diff] [blame] | 393 | // Note: Don't remove preloaded DLC images. F20 transition to provision DLC |
| 394 | // images will allow for preloading to be deprecated. |
Amin Hassani | 9a3f20c | 2020-05-25 16:38:33 -0700 | [diff] [blame] | 395 | return true; |
| 396 | } |
| 397 | |
Andrew | 0a534ed | 2020-05-06 09:59:17 -0700 | [diff] [blame] | 398 | bool DlcBase::FinishInstall(bool installed_by_ue, ErrorPtr* err) { |
| 399 | DCHECK(err); |
| 400 | DCHECK(err->get() == NULL); // Check there is no error set. |
Amin Hassani | 9a3f20c | 2020-05-25 16:38:33 -0700 | [diff] [blame] | 401 | switch (state_.state()) { |
| 402 | case DlcState::INSTALLED: |
| 403 | case DlcState::INSTALLING: |
| 404 | if (!IsVerified()) { |
| 405 | // If the image is not verified, try to verify it. This is to combat |
| 406 | // update_engine failing to call into |InstallCompleted()| even after a |
| 407 | // successful DLC installation. |
| 408 | if (Verify()) { |
| 409 | LOG(WARNING) << "Missing verification mark for DLC=" << id_ |
| 410 | << ", but verified to be a valid image."; |
| 411 | } |
| 412 | } |
Vyshu | 9e36f00 | 2020-08-31 16:42:04 +0000 | [diff] [blame] | 413 | if (IsVerified()) { |
| 414 | if (Mount(err)) |
| 415 | break; |
| 416 | // Do not |CancelInstall| on mount failure. |
Jae Hoon Kim | e9c30f7 | 2021-03-12 16:26:00 -0800 | [diff] [blame] | 417 | state_.set_last_error_code(Error::GetErrorCode(*err)); |
Vyshu | 9e36f00 | 2020-08-31 16:42:04 +0000 | [diff] [blame] | 418 | ChangeState(DlcState::NOT_INSTALLED); |
| 419 | MarkUnverified(); |
| 420 | SystemState::Get()->metrics()->SendInstallResultFailure(err); |
| 421 | LOG(ERROR) << "Mount failed during install finalization for DLC=" |
| 422 | << id_; |
| 423 | return false; |
Amin Hassani | 9a3f20c | 2020-05-25 16:38:33 -0700 | [diff] [blame] | 424 | } else { |
Jae Hoon Kim | 9253042 | 2021-03-08 15:51:45 -0800 | [diff] [blame] | 425 | // Check if the failure was because update_engine finished the |
| 426 | // installation with "noupdate". |
| 427 | if (installed_by_ue && |
| 428 | SystemState::Get()->update_engine_status().last_attempt_error() == |
| 429 | static_cast<int32_t>(update_engine::ErrorCode::kNoUpdate)) { |
| 430 | *err = Error::CreateInternal( |
| 431 | FROM_HERE, kErrorNoImageFound, |
| 432 | base::StringPrintf( |
| 433 | "Update engine could not install DLC=%s, since " |
| 434 | "Omaha could not provide the image.", |
| 435 | id_.c_str())); |
| 436 | } else { |
| 437 | // The error is empty since verification was not successful. |
| 438 | *err = Error::CreateInternal( |
| 439 | FROM_HERE, error::kFailedToVerifyImage, |
| 440 | base::StringPrintf("Cannot verify image for DLC=%s", |
| 441 | id_.c_str())); |
| 442 | } |
Amin Hassani | 40c0f11 | 2020-04-15 09:55:00 -0700 | [diff] [blame] | 443 | |
Andrew | 0a534ed | 2020-05-06 09:59:17 -0700 | [diff] [blame] | 444 | SystemState::Get()->metrics()->SendInstallResultFailure(err); |
Amin Hassani | 9a3f20c | 2020-05-25 16:38:33 -0700 | [diff] [blame] | 445 | ErrorPtr tmp_err; |
Andrew | bcc4bd8 | 2020-06-11 14:23:55 -0700 | [diff] [blame] | 446 | if (!CancelInstall(*err, &tmp_err)) |
Andrew | 936ccf6 | 2020-06-12 13:00:23 -0700 | [diff] [blame] | 447 | LOG(ERROR) << "Failed during install finalization for DLC=" << id_; |
Amin Hassani | 9a3f20c | 2020-05-25 16:38:33 -0700 | [diff] [blame] | 448 | return false; |
| 449 | } |
| 450 | case DlcState::NOT_INSTALLED: |
| 451 | // Should not try to finish install on a not-installed DLC. |
| 452 | default: |
| 453 | NOTREACHED(); |
| 454 | return false; |
| 455 | } |
| 456 | |
Amin Hassani | 40c0f11 | 2020-04-15 09:55:00 -0700 | [diff] [blame] | 457 | // Increase the ref count. |
| 458 | ref_count_->InstalledDlc(); |
| 459 | |
Amin Hassani | 9a3f20c | 2020-05-25 16:38:33 -0700 | [diff] [blame] | 460 | // Now that we are sure the image is installed, we can go ahead and set it as |
| 461 | // active. Failure to set the metadata flags should not fail the install. |
Amin Hassani | 40c0f11 | 2020-04-15 09:55:00 -0700 | [diff] [blame] | 462 | SetActiveValue(true); |
Andrew | 0a534ed | 2020-05-06 09:59:17 -0700 | [diff] [blame] | 463 | SystemState::Get()->metrics()->SendInstallResultSuccess(installed_by_ue); |
Amin Hassani | 9a3f20c | 2020-05-25 16:38:33 -0700 | [diff] [blame] | 464 | |
Amin Hassani | a69f32e | 2020-03-30 15:20:42 -0700 | [diff] [blame] | 465 | return true; |
| 466 | } |
| 467 | |
Andrew | bcc4bd8 | 2020-06-11 14:23:55 -0700 | [diff] [blame] | 468 | bool DlcBase::CancelInstall(const ErrorPtr& err_in, ErrorPtr* err) { |
Jae Hoon Kim | e9c30f7 | 2021-03-12 16:26:00 -0800 | [diff] [blame] | 469 | state_.set_last_error_code(Error::GetErrorCode(err_in)); |
Amin Hassani | 40c0f11 | 2020-04-15 09:55:00 -0700 | [diff] [blame] | 470 | ChangeState(DlcState::NOT_INSTALLED); |
| 471 | |
Amin Hassani | a69f32e | 2020-03-30 15:20:42 -0700 | [diff] [blame] | 472 | // Consider as not installed even if delete fails below, correct errors |
| 473 | // will be propagated later and should not block on further installs. |
| 474 | if (!DeleteInternal(err)) { |
Andrew | 936ccf6 | 2020-06-12 13:00:23 -0700 | [diff] [blame] | 475 | LOG(ERROR) << "Failed during install cancellation for DLC=" << id_; |
Amin Hassani | a69f32e | 2020-03-30 15:20:42 -0700 | [diff] [blame] | 476 | return false; |
| 477 | } |
| 478 | return true; |
| 479 | } |
| 480 | |
| 481 | bool DlcBase::Mount(ErrorPtr* err) { |
| 482 | string mount_point; |
| 483 | if (!SystemState::Get()->image_loader()->LoadDlcImage( |
| 484 | id_, package_, |
| 485 | SystemState::Get()->active_boot_slot() == BootSlot::Slot::A |
| 486 | ? imageloader::kSlotNameA |
| 487 | : imageloader::kSlotNameB, |
Jae Hoon Kim | 19a1866 | 2020-11-16 12:35:54 -0800 | [diff] [blame] | 488 | &mount_point, nullptr)) { |
Andrew | 0a534ed | 2020-05-06 09:59:17 -0700 | [diff] [blame] | 489 | *err = |
| 490 | Error::CreateInternal(FROM_HERE, error::kFailedToMountImage, |
| 491 | "Imageloader is unavailable for LoadDlcImage()."); |
Jae Hoon Kim | e9c30f7 | 2021-03-12 16:26:00 -0800 | [diff] [blame] | 492 | state_.set_last_error_code(Error::GetErrorCode(*err)); |
Amin Hassani | a69f32e | 2020-03-30 15:20:42 -0700 | [diff] [blame] | 493 | return false; |
| 494 | } |
| 495 | if (mount_point.empty()) { |
Andrew | 0a534ed | 2020-05-06 09:59:17 -0700 | [diff] [blame] | 496 | *err = Error::CreateInternal(FROM_HERE, error::kFailedToMountImage, |
| 497 | "Imageloader LoadDlcImage() call failed."); |
Jae Hoon Kim | e9c30f7 | 2021-03-12 16:26:00 -0800 | [diff] [blame] | 498 | state_.set_last_error_code(Error::GetErrorCode(*err)); |
Amin Hassani | a69f32e | 2020-03-30 15:20:42 -0700 | [diff] [blame] | 499 | return false; |
| 500 | } |
| 501 | mount_point_ = FilePath(mount_point); |
Jae Hoon Kim | dcadbd4 | 2020-06-25 09:21:03 -0700 | [diff] [blame] | 502 | |
| 503 | // Creates a file which holds the root mount path, allowing for indirect |
| 504 | // access for processes/scripts which can't access DBus. |
| 505 | if (manifest_.mount_file_required() && |
Jae Hoon Kim | 72fc5fc | 2020-07-22 11:25:45 -0700 | [diff] [blame] | 506 | !Prefs(prefs_package_path_).SetKey(kDlcRootMount, GetRoot().value())) { |
| 507 | // TODO(kimjae): Test this by injecting |Prefs| class. |
Jae Hoon Kim | dcadbd4 | 2020-06-25 09:21:03 -0700 | [diff] [blame] | 508 | LOG(ERROR) << "Failed to create indirect root mount file: " |
| 509 | << JoinPaths(prefs_package_path_, kDlcRootMount); |
Jae Hoon Kim | 72fc5fc | 2020-07-22 11:25:45 -0700 | [diff] [blame] | 510 | ErrorPtr tmp_err; |
| 511 | Unmount(&tmp_err); |
| 512 | return false; |
| 513 | } |
Jae Hoon Kim | dcadbd4 | 2020-06-25 09:21:03 -0700 | [diff] [blame] | 514 | |
Amin Hassani | 78a5ec8 | 2020-05-19 09:47:49 -0700 | [diff] [blame] | 515 | ChangeState(DlcState::INSTALLED); |
Amin Hassani | a69f32e | 2020-03-30 15:20:42 -0700 | [diff] [blame] | 516 | return true; |
| 517 | } |
| 518 | |
| 519 | bool DlcBase::Unmount(ErrorPtr* err) { |
| 520 | bool success = false; |
Jae Hoon Kim | 19a1866 | 2020-11-16 12:35:54 -0800 | [diff] [blame] | 521 | if (!SystemState::Get()->image_loader()->UnloadDlcImage(id_, package_, |
| 522 | &success, nullptr)) { |
Amin Hassani | 78a5ec8 | 2020-05-19 09:47:49 -0700 | [diff] [blame] | 523 | state_.set_last_error_code(kErrorInternal); |
| 524 | *err = Error::Create(FROM_HERE, state_.last_error_code(), |
Amin Hassani | a69f32e | 2020-03-30 15:20:42 -0700 | [diff] [blame] | 525 | "Imageloader is unavailable for UnloadDlcImage()."); |
| 526 | return false; |
| 527 | } |
| 528 | if (!success) { |
Amin Hassani | 78a5ec8 | 2020-05-19 09:47:49 -0700 | [diff] [blame] | 529 | state_.set_last_error_code(kErrorInternal); |
| 530 | *err = Error::Create(FROM_HERE, state_.last_error_code(), |
Amin Hassani | a69f32e | 2020-03-30 15:20:42 -0700 | [diff] [blame] | 531 | "Imageloader UnloadDlcImage() call failed."); |
| 532 | return false; |
| 533 | } |
Amin Hassani | 78a5ec8 | 2020-05-19 09:47:49 -0700 | [diff] [blame] | 534 | |
Jae Hoon Kim | dcadbd4 | 2020-06-25 09:21:03 -0700 | [diff] [blame] | 535 | if (manifest_.mount_file_required()) { |
| 536 | if (!Prefs(prefs_package_path_).Delete(kDlcRootMount)) |
| 537 | LOG(ERROR) << "Failed to delete indirect root mount file: " |
| 538 | << JoinPaths(prefs_package_path_, kDlcRootMount); |
| 539 | } |
| 540 | |
Jae Hoon Kim | f9b2c9d | 2020-07-22 12:09:13 -0700 | [diff] [blame] | 541 | mount_point_.clear(); |
Amin Hassani | a69f32e | 2020-03-30 15:20:42 -0700 | [diff] [blame] | 542 | return true; |
| 543 | } |
| 544 | |
Amin Hassani | a69f32e | 2020-03-30 15:20:42 -0700 | [diff] [blame] | 545 | bool DlcBase::IsActiveImagePresent() const { |
| 546 | return base::PathExists(GetImagePath(SystemState::Get()->active_boot_slot())); |
| 547 | } |
| 548 | |
| 549 | // Deletes all directories related to this DLC. |
| 550 | bool DlcBase::DeleteInternal(ErrorPtr* err) { |
Amin Hassani | 40c0f11 | 2020-04-15 09:55:00 -0700 | [diff] [blame] | 551 | // If we're deleting the image, we need to set it as unverified. |
| 552 | MarkUnverified(); |
| 553 | |
Amin Hassani | a69f32e | 2020-03-30 15:20:42 -0700 | [diff] [blame] | 554 | vector<string> undeleted_paths; |
Jae Hoon Kim | 0c2b6cd | 2020-04-30 13:23:16 -0700 | [diff] [blame] | 555 | for (const auto& path : GetPathsToDelete(id_)) { |
Andrew | 22064c3 | 2020-05-08 13:38:06 -0700 | [diff] [blame] | 556 | if (base::PathExists(path)) { |
hscham | 53cf73a | 2020-11-30 15:58:42 +0900 | [diff] [blame] | 557 | if (!base::DeletePathRecursively(path)) { |
Andrew | 22064c3 | 2020-05-08 13:38:06 -0700 | [diff] [blame] | 558 | PLOG(ERROR) << "Failed to delete path=" << path; |
| 559 | undeleted_paths.push_back(path.value()); |
| 560 | } else { |
| 561 | LOG(INFO) << "Deleted path=" << path; |
| 562 | } |
Amin Hassani | a69f32e | 2020-03-30 15:20:42 -0700 | [diff] [blame] | 563 | } |
| 564 | } |
Amin Hassani | f27aac0 | 2020-04-23 21:56:26 -0700 | [diff] [blame] | 565 | |
Amin Hassani | a69f32e | 2020-03-30 15:20:42 -0700 | [diff] [blame] | 566 | if (!undeleted_paths.empty()) { |
Amin Hassani | 78a5ec8 | 2020-05-19 09:47:49 -0700 | [diff] [blame] | 567 | state_.set_last_error_code(kErrorInternal); |
Amin Hassani | a69f32e | 2020-03-30 15:20:42 -0700 | [diff] [blame] | 568 | *err = Error::Create( |
Amin Hassani | 78a5ec8 | 2020-05-19 09:47:49 -0700 | [diff] [blame] | 569 | FROM_HERE, state_.last_error_code(), |
Amin Hassani | a69f32e | 2020-03-30 15:20:42 -0700 | [diff] [blame] | 570 | base::StringPrintf("DLC directories (%s) could not be deleted.", |
| 571 | base::JoinString(undeleted_paths, ",").c_str())); |
| 572 | return false; |
| 573 | } |
| 574 | return true; |
| 575 | } |
| 576 | |
Amin Hassani | 40c0f11 | 2020-04-15 09:55:00 -0700 | [diff] [blame] | 577 | bool DlcBase::Uninstall(ErrorPtr* err) { |
Amin Hassani | a69f32e | 2020-03-30 15:20:42 -0700 | [diff] [blame] | 578 | switch (state_.state()) { |
| 579 | case DlcState::NOT_INSTALLED: |
Amin Hassani | 40c0f11 | 2020-04-15 09:55:00 -0700 | [diff] [blame] | 580 | // We still have to uninstall the DLC, in case we never mounted in this |
Amin Hassani | 9a3f20c | 2020-05-25 16:38:33 -0700 | [diff] [blame] | 581 | // session. |
Amin Hassani | a69f32e | 2020-03-30 15:20:42 -0700 | [diff] [blame] | 582 | LOG(WARNING) << "Trying to uninstall not installed DLC=" << id_; |
Amin Hassani | 9a3f20c | 2020-05-25 16:38:33 -0700 | [diff] [blame] | 583 | FALLTHROUGH; |
Andrew | 91b6205 | 2020-06-24 14:57:30 -0700 | [diff] [blame] | 584 | case DlcState::INSTALLED: { |
Amin Hassani | 40c0f11 | 2020-04-15 09:55:00 -0700 | [diff] [blame] | 585 | ref_count_->UninstalledDlc(); |
Andrew | 91b6205 | 2020-06-24 14:57:30 -0700 | [diff] [blame] | 586 | ErrorPtr tmp_err; |
| 587 | Unmount(&tmp_err); |
Amin Hassani | 40c0f11 | 2020-04-15 09:55:00 -0700 | [diff] [blame] | 588 | ChangeState(DlcState::NOT_INSTALLED); |
| 589 | break; |
Andrew | 91b6205 | 2020-06-24 14:57:30 -0700 | [diff] [blame] | 590 | } |
Amin Hassani | a69f32e | 2020-03-30 15:20:42 -0700 | [diff] [blame] | 591 | case DlcState::INSTALLING: |
Amin Hassani | 40c0f11 | 2020-04-15 09:55:00 -0700 | [diff] [blame] | 592 | // We cannot uninstall the image while it is being installed by the |
Amin Hassani | 9a3f20c | 2020-05-25 16:38:33 -0700 | [diff] [blame] | 593 | // update_engine. |
Amin Hassani | 78a5ec8 | 2020-05-19 09:47:49 -0700 | [diff] [blame] | 594 | state_.set_last_error_code(kErrorBusy); |
Amin Hassani | a69f32e | 2020-03-30 15:20:42 -0700 | [diff] [blame] | 595 | *err = Error::Create( |
Amin Hassani | 78a5ec8 | 2020-05-19 09:47:49 -0700 | [diff] [blame] | 596 | FROM_HERE, state_.last_error_code(), |
Amin Hassani | 40c0f11 | 2020-04-15 09:55:00 -0700 | [diff] [blame] | 597 | base::StringPrintf("Trying to uninstall an installing DLC=%s", |
Amin Hassani | a69f32e | 2020-03-30 15:20:42 -0700 | [diff] [blame] | 598 | id_.c_str())); |
| 599 | return false; |
Amin Hassani | a69f32e | 2020-03-30 15:20:42 -0700 | [diff] [blame] | 600 | default: |
| 601 | NOTREACHED(); |
| 602 | return false; |
| 603 | } |
Amin Hassani | 40c0f11 | 2020-04-15 09:55:00 -0700 | [diff] [blame] | 604 | |
| 605 | return true; |
| 606 | } |
| 607 | |
| 608 | bool DlcBase::Purge(ErrorPtr* err) { |
Amin Hassani | f656f29 | 2020-06-08 16:20:01 -0700 | [diff] [blame] | 609 | // If the DLC is not verified, its not being updated, so there is no danger |
| 610 | // purging it. |
| 611 | auto ue_operation = |
| 612 | SystemState::Get()->update_engine_status().current_operation(); |
| 613 | bool ue_is_busy = ue_operation != update_engine::IDLE && |
| 614 | ue_operation != update_engine::UPDATED_NEED_REBOOT; |
| 615 | if (IsVerified() && ue_is_busy) { |
| 616 | *err = Error::Create(FROM_HERE, kErrorBusy, |
| 617 | "Install or update is in progress."); |
| 618 | return false; |
| 619 | } |
| 620 | |
Amin Hassani | 40c0f11 | 2020-04-15 09:55:00 -0700 | [diff] [blame] | 621 | if (!Uninstall(err)) |
| 622 | return false; |
| 623 | |
| 624 | SetActiveValue(false); |
| 625 | return DeleteInternal(err); |
| 626 | } |
| 627 | |
| 628 | bool DlcBase::ShouldPurge() { |
Amin Hassani | aafcc2d | 2020-06-08 19:21:51 -0700 | [diff] [blame] | 629 | // We can only automatically purge a DLC that is not installed. |
| 630 | return state_.state() == DlcState::NOT_INSTALLED && |
| 631 | ref_count_->ShouldPurgeDlc(); |
Amin Hassani | 40c0f11 | 2020-04-15 09:55:00 -0700 | [diff] [blame] | 632 | } |
| 633 | |
| 634 | void DlcBase::SetActiveValue(bool active) { |
| 635 | ErrorPtr tmp_err; |
| 636 | if (!SystemState::Get()->update_engine()->SetDlcActiveValue(active, id_, |
| 637 | &tmp_err)) |
| 638 | LOG(WARNING) << "Failed to set DLC=" << id_ << (active ? " " : " in") |
| 639 | << "active." |
| 640 | << (tmp_err ? Error::ToString(tmp_err) |
| 641 | : "Missing error from update engine proxy."); |
Amin Hassani | a69f32e | 2020-03-30 15:20:42 -0700 | [diff] [blame] | 642 | } |
| 643 | |
Amin Hassani | 78a5ec8 | 2020-05-19 09:47:49 -0700 | [diff] [blame] | 644 | void DlcBase::ChangeState(DlcState::State state) { |
| 645 | switch (state) { |
| 646 | case DlcState::NOT_INSTALLED: |
| 647 | state_.set_state(state); |
| 648 | state_.set_progress(0); |
| 649 | state_.clear_root_path(); |
| 650 | break; |
| 651 | |
| 652 | case DlcState::INSTALLING: |
| 653 | state_.set_state(state); |
| 654 | state_.set_progress(0); |
| 655 | state_.set_last_error_code(kErrorNone); |
| 656 | break; |
| 657 | |
| 658 | case DlcState::INSTALLED: |
| 659 | state_.set_state(state); |
| 660 | state_.set_progress(1.0); |
Jae Hoon Kim | 5b1d015 | 2020-08-03 11:23:44 -0700 | [diff] [blame] | 661 | state_.set_root_path(GetRoot().value()); |
Amin Hassani | 78a5ec8 | 2020-05-19 09:47:49 -0700 | [diff] [blame] | 662 | break; |
| 663 | |
| 664 | default: |
| 665 | NOTREACHED(); |
| 666 | } |
| 667 | |
| 668 | LOG(INFO) << "Changing DLC=" << id_ << " state to " << state_.state(); |
| 669 | SystemState::Get()->state_change_reporter()->DlcStateChanged(state_); |
| 670 | } |
| 671 | |
| 672 | void DlcBase::ChangeProgress(double progress) { |
| 673 | if (state_.state() != DlcState::INSTALLING) { |
| 674 | LOG(WARNING) << "Cannot change the progress if DLC is not being installed."; |
| 675 | return; |
| 676 | } |
| 677 | |
| 678 | // Make sure the progress is not decreased. |
| 679 | if (state_.progress() < progress) { |
| 680 | state_.set_progress(std::min(progress, 1.0)); |
| 681 | SystemState::Get()->state_change_reporter()->DlcStateChanged(state_); |
| 682 | } |
| 683 | } |
| 684 | |
Amin Hassani | a69f32e | 2020-03-30 15:20:42 -0700 | [diff] [blame] | 685 | } // namespace dlcservice |