blob: 38c38453ef21af248b424f5bae94b36f31899308 [file] [log] [blame]
Amin Hassania69f32e2020-03-30 15:20:42 -07001// 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 Hassani40c0f112020-04-15 09:55:00 -07009#include <memory>
Amin Hassania69f32e2020-03-30 15:20:42 -070010#include <utility>
11#include <vector>
12
Qijiang Fan713061e2021-03-08 15:45:12 +090013#include <base/check.h>
Qijiang Fan886c4692021-02-19 11:54:10 +090014#include <base/notreached.h>
Amin Hassania69f32e2020-03-30 15:20:42 -070015#include <base/strings/stringprintf.h>
Jae Hoon Kim760a6942020-04-15 10:01:02 -070016#include <base/strings/string_number_conversions.h>
Amin Hassania69f32e2020-03-30 15:20:42 -070017#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 Kim9a27d152020-04-10 12:50:14 -070023#include "dlcservice/prefs.h"
Amin Hassania69f32e2020-03-30 15:20:42 -070024#include "dlcservice/system_state.h"
25#include "dlcservice/utils.h"
26
Amin Hassani4ca7e1c2020-04-29 13:01:33 -070027using base::FilePath;
28using brillo::ErrorPtr;
Amin Hassania69f32e2020-03-30 15:20:42 -070029using std::string;
30using std::vector;
31
32namespace dlcservice {
Jae Hoon Kim0c2b6cd2020-04-30 13:23:16 -070033
34// static
35vector<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
Andrewa9c9e102020-05-05 12:27:35 -070041// TODO(ahassani): Instead of initialize function, create a factory method so
Amin Hassania69f32e2020-03-30 15:20:42 -070042// we can develop different types of DLC classes.
43bool DlcBase::Initialize() {
Amin Hassanife63fc22020-04-07 11:34:34 -070044 const auto* system_state = SystemState::Get();
45 const auto& manifest_dir = system_state->manifest_dir();
Amin Hassania69f32e2020-03-30 15:20:42 -070046 package_ = *ScanDirectory(manifest_dir.Append(id_)).begin();
Amin Hassanife63fc22020-04-07 11:34:34 -070047 if (!GetDlcManifest(system_state->manifest_dir(), id_, package_,
Amin Hassania69f32e2020-03-30 15:20:42 -070048 &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 Hassanife63fc22020-04-07 11:34:34 -070054 const auto& content_dir = system_state->content_dir();
Jae Hoon Kimdcadbd42020-06-25 09:21:03 -070055 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 Kim0d4ff622020-05-14 14:47:40 -070059 preloaded_image_path_ = JoinPaths(system_state->preloaded_content_dir(), id_,
60 package_, kDlcImageFileName);
Amin Hassani40c0f112020-04-15 09:55:00 -070061 ref_count_ = RefCountInterface::Create(manifest_.used_by(), prefs_path_);
Amin Hassania69f32e2020-03-30 15:20:42 -070062
Amin Hassanif27aac02020-04-23 21:56:26 -070063 state_.set_state(DlcState::NOT_INSTALLED);
Amin Hassani78a5ec82020-05-19 09:47:49 -070064 state_.set_id(id_);
65 state_.set_progress(0);
66 state_.set_last_error_code(kErrorNone);
Amin Hassania69f32e2020-03-30 15:20:42 -070067
Jae Hoon Kim46304202020-07-17 15:15:45 -070068 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 Khota32388f92020-11-10 09:13:29 -080074 state_.set_is_verified(
75 Prefs(*this, system_state->active_boot_slot()).Exists(kDlcPrefVerified));
Amin Hassania69f32e2020-03-30 15:20:42 -070076 return true;
77}
78
Amin Hassani4ca7e1c2020-04-29 13:01:33 -070079const DlcId& DlcBase::GetId() const {
Jae Hoon Kime567b9e2020-04-08 14:43:59 -070080 return id_;
81}
82
Amin Hassanid5fc8b22020-04-29 12:44:52 -070083const std::string& DlcBase::GetName() const {
84 return manifest_.name();
85}
86
87const std::string& DlcBase::GetDescription() const {
88 return manifest_.description();
89}
90
Amin Hassania69f32e2020-03-30 15:20:42 -070091DlcState DlcBase::GetState() const {
92 return state_;
93}
94
95bool DlcBase::IsInstalling() const {
96 return state_.state() == DlcState::INSTALLING;
97}
Jae Hoon Kim0d057fb2020-04-13 17:44:38 -070098
Amin Hassania69f32e2020-03-30 15:20:42 -070099bool DlcBase::IsInstalled() const {
100 return state_.state() == DlcState::INSTALLED;
101}
102
Amin Hassanif27aac02020-04-23 21:56:26 -0700103bool DlcBase::IsVerified() const {
Vyshu Khota32388f92020-11-10 09:13:29 -0800104 return state_.is_verified();
Jae Hoon Kim0d057fb2020-04-13 17:44:38 -0700105}
106
Amin Hassanid5fc8b22020-04-29 12:44:52 -0700107bool 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
116uint64_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 Hassania69f32e2020-03-30 15:20:42 -0700131bool DlcBase::IsPreloadAllowed() const {
Jae Hoon Kim9318f342020-09-10 14:38:13 -0700132 return manifest_.preload_allowed() &&
133 !SystemState::Get()->system_properties()->IsOfficialBuild();
Amin Hassania69f32e2020-03-30 15:20:42 -0700134}
135
136base::FilePath DlcBase::GetRoot() const {
Jae Hoon Kim2c61e9e2020-04-07 15:03:35 -0700137 if (mount_point_.empty())
138 return {};
Jae Hoon Kime567b9e2020-04-08 14:43:59 -0700139 return JoinPaths(mount_point_, kRootDirectoryInsideDlcModule);
Amin Hassania69f32e2020-03-30 15:20:42 -0700140}
141
Amin Hassanif27aac02020-04-23 21:56:26 -0700142bool DlcBase::InstallCompleted(ErrorPtr* err) {
Amin Hassani6b7a9aa2020-05-29 14:23:47 -0700143 if (!MarkVerified()) {
Amin Hassani78a5ec82020-05-19 09:47:49 -0700144 state_.set_last_error_code(kErrorInternal);
Jae Hoon Kim9a27d152020-04-10 12:50:14 -0700145 *err = Error::Create(
Amin Hassani78a5ec82020-05-19 09:47:49 -0700146 FROM_HERE, state_.last_error_code(),
Amin Hassanif27aac02020-04-23 21:56:26 -0700147 base::StringPrintf("Failed to mark active DLC=%s as verified.",
148 id_.c_str()));
149 return false;
150 }
Amin Hassanif27aac02020-04-23 21:56:26 -0700151 return true;
152}
153
Amin Hassani78a5ec82020-05-19 09:47:49 -0700154bool DlcBase::UpdateCompleted(ErrorPtr* err) {
Amin Hassanif27aac02020-04-23 21:56:26 -0700155 if (!Prefs(*this, SystemState::Get()->inactive_boot_slot())
156 .Create(kDlcPrefVerified)) {
157 *err = Error::Create(
Andrew9aa06442020-05-06 10:33:46 -0700158 FROM_HERE, kErrorInternal,
Amin Hassanif27aac02020-04-23 21:56:26 -0700159 base::StringPrintf("Failed to mark inactive DLC=%s as verified.",
160 id_.c_str()));
Jae Hoon Kim9a27d152020-04-10 12:50:14 -0700161 return false;
162 }
163 return true;
164}
165
Amin Hassania69f32e2020-03-30 15:20:42 -0700166FilePath DlcBase::GetImagePath(BootSlot::Slot slot) const {
Jae Hoon Kime567b9e2020-04-08 14:43:59 -0700167 return JoinPaths(content_package_path_, BootSlot::ToString(slot),
168 kDlcImageFileName);
Amin Hassania69f32e2020-03-30 15:20:42 -0700169}
170
Amin Hassanic0867b62020-04-16 09:44:34 -0700171bool DlcBase::CreateDlc(ErrorPtr* err) {
Amin Hassania69f32e2020-03-30 15:20:42 -0700172 // Create content directories.
Amin Hassanic0867b62020-04-16 09:44:34 -0700173 for (const auto& path :
174 {content_id_path_, content_package_path_, prefs_path_}) {
Amin Hassania69f32e2020-03-30 15:20:42 -0700175 if (!CreateDir(path)) {
Andrew0a534ed2020-05-06 09:59:17 -0700176 *err = Error::CreateInternal(
177 FROM_HERE, error::kFailedToCreateDirectory,
Amin Hassania69f32e2020-03-30 15:20:42 -0700178 base::StringPrintf("Failed to create directory %s for DLC=%s",
179 path.value().c_str(), id_.c_str()));
Jae Hoon Kime9c30f72021-03-12 16:26:00 -0800180 state_.set_last_error_code(Error::GetErrorCode(*err));
Amin Hassania69f32e2020-03-30 15:20:42 -0700181 return false;
182 }
183 }
184
Amin Hassania69f32e2020-03-30 15:20:42 -0700185 // Creates image A and B.
186 for (const auto& slot : {BootSlot::Slot::A, BootSlot::Slot::B}) {
187 FilePath image_path = GetImagePath(slot);
Vyshu Khotafe574082021-03-09 16:32:23 -0500188 if (!CreateFile(image_path, manifest_.size())) {
Amin Hassani78a5ec82020-05-19 09:47:49 -0700189 state_.set_last_error_code(kErrorAllocation);
Amin Hassania69f32e2020-03-30 15:20:42 -0700190 *err = Error::Create(
Amin Hassani78a5ec82020-05-19 09:47:49 -0700191 FROM_HERE, state_.last_error_code(),
Amin Hassania69f32e2020-03-30 15:20:42 -0700192 base::StringPrintf("Failed to create image file %s for DLC=%s",
193 image_path.value().c_str(), id_.c_str()));
194 return false;
Vyshu Khotafe574082021-03-09 16:32:23 -0500195 } 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 Hassania69f32e2020-03-30 15:20:42 -0700198 }
199 }
200
Amin Hassania69f32e2020-03-30 15:20:42 -0700201 return true;
202}
203
Amin Hassani48567772020-05-20 16:21:11 -0700204bool 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 Hassania69f32e2020-03-30 15:20:42 -0700212 }
213
Amin Hassani48567772020-05-20 16:21:11 -0700214 if (!IsVerified()) {
215 return false;
216 }
217
218 const FilePath& inactive_image_path =
219 GetImagePath(SystemState::Get()->inactive_boot_slot());
Vyshu Khotafe574082021-03-09 16:32:23 -0500220 if (!CreateFile(inactive_image_path, manifest_.size())) {
Amin Hassani48567772020-05-20 16:21:11 -0700221 LOG(ERROR) << "Failed to create inactive image "
222 << inactive_image_path.value() << " when making DLC=" << id_
223 << " ready for update.";
224 return false;
Vyshu Khotafe574082021-03-09 16:32:23 -0500225 } 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 Hassania69f32e2020-03-30 15:20:42 -0700229 }
Vyshu Khotafe574082021-03-09 16:32:23 -0500230
Amin Hassania69f32e2020-03-30 15:20:42 -0700231 return true;
232}
233
Amin Hassani6b7a9aa2020-05-29 14:23:47 -0700234bool DlcBase::MarkVerified() {
Vyshu Khota32388f92020-11-10 09:13:29 -0800235 state_.set_is_verified(true);
Amin Hassani6b7a9aa2020-05-29 14:23:47 -0700236 return Prefs(*this, SystemState::Get()->active_boot_slot())
237 .Create(kDlcPrefVerified);
238}
239
240bool DlcBase::MarkUnverified() {
Vyshu Khota32388f92020-11-10 09:13:29 -0800241 state_.set_is_verified(false);
Amin Hassani6b7a9aa2020-05-29 14:23:47 -0700242 return Prefs(*this, SystemState::Get()->active_boot_slot())
243 .Delete(kDlcPrefVerified);
Amin Hassani28ed9002020-05-28 12:37:01 -0700244}
245
Jae Hoon Kim8ab26492020-05-07 13:49:36 -0700246bool DlcBase::Verify() {
247 auto image_path = GetImagePath(SystemState::Get()->active_boot_slot());
248 vector<uint8_t> image_sha256;
Amin Hassanif2efc5a2020-05-25 21:22:57 -0700249 if (!HashFile(image_path, manifest_.size(), &image_sha256)) {
Jae Hoon Kim8ab26492020-05-07 13:49:36 -0700250 LOG(ERROR) << "Failed to hash image file: " << image_path.value();
251 return false;
252 }
Amin Hassanif2efc5a2020-05-25 21:22:57 -0700253
Jae Hoon Kim8ab26492020-05-07 13:49:36 -0700254 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 Hassani6b7a9aa2020-05-29 14:23:47 -0700264
265 if (!MarkVerified()) {
266 LOG(WARNING) << "Failed to mark the image as verified, but temporarily"
267 << " we assume the image is verified.";
Jae Hoon Kim8ab26492020-05-07 13:49:36 -0700268 }
269 return true;
270}
271
Jae Hoon Kim0d4ff622020-05-14 14:47:40 -0700272bool DlcBase::PreloadedCopier(ErrorPtr* err) {
Amin Hassani28ed9002020-05-28 12:37:01 -0700273 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 Hassania69f32e2020-03-30 15:20:42 -0700287 }
288
Amin Hassani28ed9002020-05-28 12:37:01 -0700289 // 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 Hassania69f32e2020-03-30 15:20:42 -0700293 // operation can be a move.
Amin Hassani28ed9002020-05-28 12:37:01 -0700294 FilePath image_path = GetImagePath(SystemState::Get()->active_boot_slot());
Amin Hassani0d3ab932020-04-26 17:35:04 -0700295 vector<uint8_t> image_sha256;
Amin Hassani28ed9002020-05-28 12:37:01 -0700296 if (!CopyAndHashFile(preloaded_image_path_, image_path, manifest_.size(),
Amin Hassanif2efc5a2020-05-25 21:22:57 -0700297 &image_sha256)) {
Jae Hoon Kim0d4ff622020-05-14 14:47:40 -0700298 auto err_str =
299 base::StringPrintf("Failed to copy preload DLC (%s) into path %s",
Amin Hassani28ed9002020-05-28 12:37:01 -0700300 id_.c_str(), image_path.value().c_str());
Jae Hoon Kim0d4ff622020-05-14 14:47:40 -0700301 *err = Error::Create(FROM_HERE, kErrorInternal, err_str);
Jae Hoon Kim760a6942020-04-15 10:01:02 -0700302 return false;
303 }
304
Jae Hoon Kim0d4ff622020-05-14 14:47:40 -0700305 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 Kim760a6942020-04-15 10:01:02 -0700315 return false;
316 }
317
Jae Hoon Kimf49b6d72020-06-15 11:29:24 -0700318 if (!MarkVerified())
319 LOG(ERROR) << "Failed to mark the image verified for DLC=" << id_;
Jae Hoon Kim0d057fb2020-04-13 17:44:38 -0700320
Amin Hassania69f32e2020-03-30 15:20:42 -0700321 return true;
322}
323
Amin Hassani9a3f20c2020-05-25 16:38:33 -0700324bool DlcBase::Install(ErrorPtr* err) {
Amin Hassania69f32e2020-03-30 15:20:42 -0700325 switch (state_.state()) {
Jae Hoon Kime9a5e132020-06-03 11:55:05 -0700326 case DlcState::NOT_INSTALLED: {
327 bool active_image_existed = IsActiveImagePresent();
Amin Hassanic0867b62020-04-16 09:44:34 -0700328 // 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)) {
Andrewbcc4bd82020-06-11 14:23:55 -0700331 ErrorPtr tmp_err;
332 if (!CancelInstall(*err, &tmp_err))
333 LOG(ERROR) << "Failed to cancel the install correctly.";
Amin Hassania69f32e2020-03-30 15:20:42 -0700334 return false;
335 }
Jae Hoon Kim099c2f92020-07-24 10:06:38 -0700336 // Only set the DLC installing after creation is successful to have finer
337 // control of state changes.
338 ChangeState(DlcState::INSTALLING);
Amin Hassani9a3f20c2020-05-25 16:38:33 -0700339
Jae Hoon Kimf49b6d72020-06-15 11:29:24 -0700340 // 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 Hassani9a3f20c2020-05-25 16:38:33 -0700355 if (IsPreloadAllowed() && base::PathExists(preloaded_image_path_)) {
Jae Hoon Kimf49b6d72020-06-15 11:29:24 -0700356 if (!PreloadedCopier(err)) {
357 LOG(ERROR)
358 << "Preloading failed, so assuming installation failed for DLC="
359 << id_;
Jae Hoon Kim13e61512020-06-26 16:36:10 -0700360 ErrorPtr tmp_err;
361 if (!CancelInstall(*err, &tmp_err))
362 LOG(ERROR) << "Failed to cancel the install from preloading.";
Amin Hassani9a3f20c2020-05-25 16:38:33 -0700363 return false;
Amin Hassanic0867b62020-04-16 09:44:34 -0700364 }
Jae Hoon Kimf49b6d72020-06-15 11:29:24 -0700365 LOG(INFO) << "Preloading DLC=" << id_;
366 break;
Amin Hassanic0867b62020-04-16 09:44:34 -0700367 }
Amin Hassani9a3f20c2020-05-25 16:38:33 -0700368
Jae Hoon Kima9ce3f82020-07-24 11:39:21 -0700369 // 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 Kime9a5e132020-06-03 11:55:05 -0700372 }
Amin Hassania69f32e2020-03-30 15:20:42 -0700373 case DlcState::INSTALLING:
Amin Hassani9a3f20c2020-05-25 16:38:33 -0700374 // If the image is already in this state, nothing need to be done. It is
375 // already being installed.
Andrew0a534ed2020-05-06 09:59:17 -0700376 // 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 Hassani9a3f20c2020-05-25 16:38:33 -0700378 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 Hassania69f32e2020-03-30 15:20:42 -0700383 default:
384 NOTREACHED();
385 return false;
386 }
Amin Hassani9a3f20c2020-05-25 16:38:33 -0700387
388 // Let's try to finish the installation.
Andrew0a534ed2020-05-06 09:59:17 -0700389 if (!FinishInstall(/*installed_by_ue=*/false, err)) {
Amin Hassani9a3f20c2020-05-25 16:38:33 -0700390 return false;
391 }
392
Jae Hoon Kim92e43e42020-06-12 15:26:30 -0700393 // Note: Don't remove preloaded DLC images. F20 transition to provision DLC
394 // images will allow for preloading to be deprecated.
Amin Hassani9a3f20c2020-05-25 16:38:33 -0700395 return true;
396}
397
Andrew0a534ed2020-05-06 09:59:17 -0700398bool DlcBase::FinishInstall(bool installed_by_ue, ErrorPtr* err) {
399 DCHECK(err);
400 DCHECK(err->get() == NULL); // Check there is no error set.
Amin Hassani9a3f20c2020-05-25 16:38:33 -0700401 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 }
Vyshu9e36f002020-08-31 16:42:04 +0000413 if (IsVerified()) {
414 if (Mount(err))
415 break;
416 // Do not |CancelInstall| on mount failure.
Jae Hoon Kime9c30f72021-03-12 16:26:00 -0800417 state_.set_last_error_code(Error::GetErrorCode(*err));
Vyshu9e36f002020-08-31 16:42:04 +0000418 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 Hassani9a3f20c2020-05-25 16:38:33 -0700424 } else {
Jae Hoon Kim92530422021-03-08 15:51:45 -0800425 // 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 Hassani40c0f112020-04-15 09:55:00 -0700443
Andrew0a534ed2020-05-06 09:59:17 -0700444 SystemState::Get()->metrics()->SendInstallResultFailure(err);
Amin Hassani9a3f20c2020-05-25 16:38:33 -0700445 ErrorPtr tmp_err;
Andrewbcc4bd82020-06-11 14:23:55 -0700446 if (!CancelInstall(*err, &tmp_err))
Andrew936ccf62020-06-12 13:00:23 -0700447 LOG(ERROR) << "Failed during install finalization for DLC=" << id_;
Amin Hassani9a3f20c2020-05-25 16:38:33 -0700448 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 Hassani40c0f112020-04-15 09:55:00 -0700457 // Increase the ref count.
458 ref_count_->InstalledDlc();
459
Amin Hassani9a3f20c2020-05-25 16:38:33 -0700460 // 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 Hassani40c0f112020-04-15 09:55:00 -0700462 SetActiveValue(true);
Andrew0a534ed2020-05-06 09:59:17 -0700463 SystemState::Get()->metrics()->SendInstallResultSuccess(installed_by_ue);
Amin Hassani9a3f20c2020-05-25 16:38:33 -0700464
Amin Hassania69f32e2020-03-30 15:20:42 -0700465 return true;
466}
467
Andrewbcc4bd82020-06-11 14:23:55 -0700468bool DlcBase::CancelInstall(const ErrorPtr& err_in, ErrorPtr* err) {
Jae Hoon Kime9c30f72021-03-12 16:26:00 -0800469 state_.set_last_error_code(Error::GetErrorCode(err_in));
Amin Hassani40c0f112020-04-15 09:55:00 -0700470 ChangeState(DlcState::NOT_INSTALLED);
471
Amin Hassania69f32e2020-03-30 15:20:42 -0700472 // 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)) {
Andrew936ccf62020-06-12 13:00:23 -0700475 LOG(ERROR) << "Failed during install cancellation for DLC=" << id_;
Amin Hassania69f32e2020-03-30 15:20:42 -0700476 return false;
477 }
478 return true;
479}
480
481bool 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 Kim19a18662020-11-16 12:35:54 -0800488 &mount_point, nullptr)) {
Andrew0a534ed2020-05-06 09:59:17 -0700489 *err =
490 Error::CreateInternal(FROM_HERE, error::kFailedToMountImage,
491 "Imageloader is unavailable for LoadDlcImage().");
Jae Hoon Kime9c30f72021-03-12 16:26:00 -0800492 state_.set_last_error_code(Error::GetErrorCode(*err));
Amin Hassania69f32e2020-03-30 15:20:42 -0700493 return false;
494 }
495 if (mount_point.empty()) {
Andrew0a534ed2020-05-06 09:59:17 -0700496 *err = Error::CreateInternal(FROM_HERE, error::kFailedToMountImage,
497 "Imageloader LoadDlcImage() call failed.");
Jae Hoon Kime9c30f72021-03-12 16:26:00 -0800498 state_.set_last_error_code(Error::GetErrorCode(*err));
Amin Hassania69f32e2020-03-30 15:20:42 -0700499 return false;
500 }
501 mount_point_ = FilePath(mount_point);
Jae Hoon Kimdcadbd42020-06-25 09:21:03 -0700502
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 Kim72fc5fc2020-07-22 11:25:45 -0700506 !Prefs(prefs_package_path_).SetKey(kDlcRootMount, GetRoot().value())) {
507 // TODO(kimjae): Test this by injecting |Prefs| class.
Jae Hoon Kimdcadbd42020-06-25 09:21:03 -0700508 LOG(ERROR) << "Failed to create indirect root mount file: "
509 << JoinPaths(prefs_package_path_, kDlcRootMount);
Jae Hoon Kim72fc5fc2020-07-22 11:25:45 -0700510 ErrorPtr tmp_err;
511 Unmount(&tmp_err);
512 return false;
513 }
Jae Hoon Kimdcadbd42020-06-25 09:21:03 -0700514
Amin Hassani78a5ec82020-05-19 09:47:49 -0700515 ChangeState(DlcState::INSTALLED);
Amin Hassania69f32e2020-03-30 15:20:42 -0700516 return true;
517}
518
519bool DlcBase::Unmount(ErrorPtr* err) {
520 bool success = false;
Jae Hoon Kim19a18662020-11-16 12:35:54 -0800521 if (!SystemState::Get()->image_loader()->UnloadDlcImage(id_, package_,
522 &success, nullptr)) {
Amin Hassani78a5ec82020-05-19 09:47:49 -0700523 state_.set_last_error_code(kErrorInternal);
524 *err = Error::Create(FROM_HERE, state_.last_error_code(),
Amin Hassania69f32e2020-03-30 15:20:42 -0700525 "Imageloader is unavailable for UnloadDlcImage().");
526 return false;
527 }
528 if (!success) {
Amin Hassani78a5ec82020-05-19 09:47:49 -0700529 state_.set_last_error_code(kErrorInternal);
530 *err = Error::Create(FROM_HERE, state_.last_error_code(),
Amin Hassania69f32e2020-03-30 15:20:42 -0700531 "Imageloader UnloadDlcImage() call failed.");
532 return false;
533 }
Amin Hassani78a5ec82020-05-19 09:47:49 -0700534
Jae Hoon Kimdcadbd42020-06-25 09:21:03 -0700535 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 Kimf9b2c9d2020-07-22 12:09:13 -0700541 mount_point_.clear();
Amin Hassania69f32e2020-03-30 15:20:42 -0700542 return true;
543}
544
Amin Hassania69f32e2020-03-30 15:20:42 -0700545bool DlcBase::IsActiveImagePresent() const {
546 return base::PathExists(GetImagePath(SystemState::Get()->active_boot_slot()));
547}
548
549// Deletes all directories related to this DLC.
550bool DlcBase::DeleteInternal(ErrorPtr* err) {
Amin Hassani40c0f112020-04-15 09:55:00 -0700551 // If we're deleting the image, we need to set it as unverified.
552 MarkUnverified();
553
Amin Hassania69f32e2020-03-30 15:20:42 -0700554 vector<string> undeleted_paths;
Jae Hoon Kim0c2b6cd2020-04-30 13:23:16 -0700555 for (const auto& path : GetPathsToDelete(id_)) {
Andrew22064c32020-05-08 13:38:06 -0700556 if (base::PathExists(path)) {
hscham53cf73a2020-11-30 15:58:42 +0900557 if (!base::DeletePathRecursively(path)) {
Andrew22064c32020-05-08 13:38:06 -0700558 PLOG(ERROR) << "Failed to delete path=" << path;
559 undeleted_paths.push_back(path.value());
560 } else {
561 LOG(INFO) << "Deleted path=" << path;
562 }
Amin Hassania69f32e2020-03-30 15:20:42 -0700563 }
564 }
Amin Hassanif27aac02020-04-23 21:56:26 -0700565
Amin Hassania69f32e2020-03-30 15:20:42 -0700566 if (!undeleted_paths.empty()) {
Amin Hassani78a5ec82020-05-19 09:47:49 -0700567 state_.set_last_error_code(kErrorInternal);
Amin Hassania69f32e2020-03-30 15:20:42 -0700568 *err = Error::Create(
Amin Hassani78a5ec82020-05-19 09:47:49 -0700569 FROM_HERE, state_.last_error_code(),
Amin Hassania69f32e2020-03-30 15:20:42 -0700570 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 Hassani40c0f112020-04-15 09:55:00 -0700577bool DlcBase::Uninstall(ErrorPtr* err) {
Amin Hassania69f32e2020-03-30 15:20:42 -0700578 switch (state_.state()) {
579 case DlcState::NOT_INSTALLED:
Amin Hassani40c0f112020-04-15 09:55:00 -0700580 // We still have to uninstall the DLC, in case we never mounted in this
Amin Hassani9a3f20c2020-05-25 16:38:33 -0700581 // session.
Amin Hassania69f32e2020-03-30 15:20:42 -0700582 LOG(WARNING) << "Trying to uninstall not installed DLC=" << id_;
Amin Hassani9a3f20c2020-05-25 16:38:33 -0700583 FALLTHROUGH;
Andrew91b62052020-06-24 14:57:30 -0700584 case DlcState::INSTALLED: {
Amin Hassani40c0f112020-04-15 09:55:00 -0700585 ref_count_->UninstalledDlc();
Andrew91b62052020-06-24 14:57:30 -0700586 ErrorPtr tmp_err;
587 Unmount(&tmp_err);
Amin Hassani40c0f112020-04-15 09:55:00 -0700588 ChangeState(DlcState::NOT_INSTALLED);
589 break;
Andrew91b62052020-06-24 14:57:30 -0700590 }
Amin Hassania69f32e2020-03-30 15:20:42 -0700591 case DlcState::INSTALLING:
Amin Hassani40c0f112020-04-15 09:55:00 -0700592 // We cannot uninstall the image while it is being installed by the
Amin Hassani9a3f20c2020-05-25 16:38:33 -0700593 // update_engine.
Amin Hassani78a5ec82020-05-19 09:47:49 -0700594 state_.set_last_error_code(kErrorBusy);
Amin Hassania69f32e2020-03-30 15:20:42 -0700595 *err = Error::Create(
Amin Hassani78a5ec82020-05-19 09:47:49 -0700596 FROM_HERE, state_.last_error_code(),
Amin Hassani40c0f112020-04-15 09:55:00 -0700597 base::StringPrintf("Trying to uninstall an installing DLC=%s",
Amin Hassania69f32e2020-03-30 15:20:42 -0700598 id_.c_str()));
599 return false;
Amin Hassania69f32e2020-03-30 15:20:42 -0700600 default:
601 NOTREACHED();
602 return false;
603 }
Amin Hassani40c0f112020-04-15 09:55:00 -0700604
605 return true;
606}
607
608bool DlcBase::Purge(ErrorPtr* err) {
Amin Hassanif656f292020-06-08 16:20:01 -0700609 // 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 Hassani40c0f112020-04-15 09:55:00 -0700621 if (!Uninstall(err))
622 return false;
623
624 SetActiveValue(false);
625 return DeleteInternal(err);
626}
627
628bool DlcBase::ShouldPurge() {
Amin Hassaniaafcc2d2020-06-08 19:21:51 -0700629 // We can only automatically purge a DLC that is not installed.
630 return state_.state() == DlcState::NOT_INSTALLED &&
631 ref_count_->ShouldPurgeDlc();
Amin Hassani40c0f112020-04-15 09:55:00 -0700632}
633
634void 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 Hassania69f32e2020-03-30 15:20:42 -0700642}
643
Amin Hassani78a5ec82020-05-19 09:47:49 -0700644void 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 Kim5b1d0152020-08-03 11:23:44 -0700661 state_.set_root_path(GetRoot().value());
Amin Hassani78a5ec82020-05-19 09:47:49 -0700662 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
672void 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 Hassania69f32e2020-03-30 15:20:42 -0700685} // namespace dlcservice