1 /*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "gsi_service.h"
18
19 #include <errno.h>
20 #include <linux/fs.h>
21 #include <sys/ioctl.h>
22 #include <sys/stat.h>
23 #include <sys/statvfs.h>
24 #include <sys/types.h>
25 #include <sys/vfs.h>
26 #include <unistd.h>
27
28 #include <chrono>
29 #include <string>
30 #include <vector>
31
32 #include <android-base/file.h>
33 #include <android-base/logging.h>
34 #include <android-base/stringprintf.h>
35 #include <android-base/strings.h>
36 #include <android/gsi/IGsiService.h>
37 #include <ext4_utils/ext4_utils.h>
38 #include <fs_mgr.h>
39 #include <fs_mgr_dm_linear.h>
40 #include <fstab/fstab.h>
41 #include <libdm/dm.h>
42 #include <libfiemap_writer/fiemap_writer.h>
43 #include <logwrap/logwrap.h>
44 #include <private/android_filesystem_config.h>
45
46 #include "file_paths.h"
47 #include "libgsi_private.h"
48
49 namespace android {
50 namespace gsi {
51
52 using namespace std::literals;
53 using namespace android::dm;
54 using namespace android::fs_mgr;
55 using namespace android::fiemap_writer;
56 using android::base::StringPrintf;
57 using android::base::unique_fd;
58
59 static constexpr char kUserdataDevice[] = "/dev/block/by-name/userdata";
60
61 // The default size of userdata.img for GSI.
62 // We are looking for /data to have atleast 40% free space
63 static constexpr uint32_t kMinimumFreeSpaceThreshold = 40;
64 // We determine the fragmentation by making sure the files
65 // we create don't have more than 16 extents.
66 static constexpr uint32_t kMaximumExtents = 512;
67 // Default userdata image size.
68 static constexpr int64_t kDefaultUserdataSize = int64_t(8) * 1024 * 1024 * 1024;
69 static constexpr std::chrono::milliseconds kDmTimeout = 5000ms;
70
Register()71 void GsiService::Register() {
72 auto ret = android::BinderService<GsiService>::publish();
73 if (ret != android::OK) {
74 LOG(FATAL) << "Could not register gsi service: " << ret;
75 }
76 }
77
GsiService()78 GsiService::GsiService() {
79 progress_ = {};
80 }
81
~GsiService()82 GsiService::~GsiService() {
83 PostInstallCleanup();
84 }
85
86 #define ENFORCE_SYSTEM \
87 do { \
88 binder::Status status = CheckUid(); \
89 if (!status.isOk()) return status; \
90 } while (0)
91
92 #define ENFORCE_SYSTEM_OR_SHELL \
93 do { \
94 binder::Status status = CheckUid(AccessLevel::SystemOrShell); \
95 if (!status.isOk()) return status; \
96 } while (0)
97
startGsiInstall(int64_t gsiSize,int64_t userdataSize,bool wipeUserdata,int * _aidl_return)98 binder::Status GsiService::startGsiInstall(int64_t gsiSize, int64_t userdataSize, bool wipeUserdata,
99 int* _aidl_return) {
100 GsiInstallParams params;
101 params.gsiSize = gsiSize;
102 params.userdataSize = userdataSize;
103 params.wipeUserdata = wipeUserdata;
104 return beginGsiInstall(params, _aidl_return);
105 }
106
beginGsiInstall(const GsiInstallParams & given_params,int * _aidl_return)107 binder::Status GsiService::beginGsiInstall(const GsiInstallParams& given_params, int* _aidl_return) {
108 ENFORCE_SYSTEM;
109 std::lock_guard<std::mutex> guard(main_lock_);
110
111 // Make sure any interrupted installations are cleaned up.
112 PostInstallCleanup();
113
114 // Do some precursor validation on the arguments before diving into the
115 // install process.
116 GsiInstallParams params = given_params;
117 if (int status = ValidateInstallParams(¶ms)) {
118 *_aidl_return = status;
119 return binder::Status::ok();
120 }
121
122 int status = StartInstall(params);
123 if (status != INSTALL_OK) {
124 // Perform local cleanup and delete any lingering files.
125 PostInstallCleanup();
126 RemoveGsiFiles(params.installDir, wipe_userdata_on_failure_);
127 }
128 *_aidl_return = status;
129
130 // Clear the progress indicator.
131 UpdateProgress(STATUS_NO_OPERATION, 0);
132 return binder::Status::ok();
133 }
134
commitGsiChunkFromStream(const android::os::ParcelFileDescriptor & stream,int64_t bytes,bool * _aidl_return)135 binder::Status GsiService::commitGsiChunkFromStream(const android::os::ParcelFileDescriptor& stream,
136 int64_t bytes, bool* _aidl_return) {
137 ENFORCE_SYSTEM;
138 std::lock_guard<std::mutex> guard(main_lock_);
139
140 *_aidl_return = CommitGsiChunk(stream.get(), bytes);
141
142 // Clear the progress indicator.
143 UpdateProgress(STATUS_NO_OPERATION, 0);
144 return binder::Status::ok();
145 }
146
StartAsyncOperation(const std::string & step,int64_t total_bytes)147 void GsiService::StartAsyncOperation(const std::string& step, int64_t total_bytes) {
148 std::lock_guard<std::mutex> guard(progress_lock_);
149
150 progress_.step = step;
151 progress_.status = STATUS_WORKING;
152 progress_.bytes_processed = 0;
153 progress_.total_bytes = total_bytes;
154 }
155
UpdateProgress(int status,int64_t bytes_processed)156 void GsiService::UpdateProgress(int status, int64_t bytes_processed) {
157 std::lock_guard<std::mutex> guard(progress_lock_);
158
159 progress_.status = status;
160 if (status == STATUS_COMPLETE) {
161 progress_.bytes_processed = progress_.total_bytes;
162 } else {
163 progress_.bytes_processed = bytes_processed;
164 }
165 }
166
getInstallProgress(::android::gsi::GsiProgress * _aidl_return)167 binder::Status GsiService::getInstallProgress(::android::gsi::GsiProgress* _aidl_return) {
168 ENFORCE_SYSTEM;
169 std::lock_guard<std::mutex> guard(progress_lock_);
170
171 *_aidl_return = progress_;
172 return binder::Status::ok();
173 }
174
commitGsiChunkFromMemory(const std::vector<uint8_t> & bytes,bool * _aidl_return)175 binder::Status GsiService::commitGsiChunkFromMemory(const std::vector<uint8_t>& bytes,
176 bool* _aidl_return) {
177 ENFORCE_SYSTEM;
178 std::lock_guard<std::mutex> guard(main_lock_);
179
180 *_aidl_return = CommitGsiChunk(bytes.data(), bytes.size());
181 return binder::Status::ok();
182 }
183
setGsiBootable(bool one_shot,int * _aidl_return)184 binder::Status GsiService::setGsiBootable(bool one_shot, int* _aidl_return) {
185 std::lock_guard<std::mutex> guard(main_lock_);
186
187 if (installing_) {
188 ENFORCE_SYSTEM;
189 int error = SetGsiBootable(one_shot);
190 PostInstallCleanup();
191 if (error) {
192 RemoveGsiFiles(install_dir_, wipe_userdata_on_failure_);
193 *_aidl_return = error;
194 } else {
195 *_aidl_return = INSTALL_OK;
196 }
197 } else {
198 ENFORCE_SYSTEM_OR_SHELL;
199 *_aidl_return = ReenableGsi(one_shot);
200 PostInstallCleanup();
201 }
202
203 return binder::Status::ok();
204 }
205
isGsiEnabled(bool * _aidl_return)206 binder::Status GsiService::isGsiEnabled(bool* _aidl_return) {
207 ENFORCE_SYSTEM_OR_SHELL;
208 std::lock_guard<std::mutex> guard(main_lock_);
209 std::string boot_key;
210 if (!GetInstallStatus(&boot_key)) {
211 *_aidl_return = false;
212 } else {
213 *_aidl_return = (boot_key == kInstallStatusOk);
214 }
215 return binder::Status::ok();
216 }
217
removeGsiInstall(bool * _aidl_return)218 binder::Status GsiService::removeGsiInstall(bool* _aidl_return) {
219 ENFORCE_SYSTEM_OR_SHELL;
220 std::lock_guard<std::mutex> guard(main_lock_);
221
222 // Just in case an install was left hanging.
223 std::string install_dir;
224 if (installing_) {
225 install_dir = install_dir_;
226 PostInstallCleanup();
227 } else {
228 install_dir = GetInstalledImageDir();
229 }
230
231 if (IsGsiRunning()) {
232 // Can't remove gsi files while running.
233 *_aidl_return = UninstallGsi();
234 } else {
235 *_aidl_return = RemoveGsiFiles(install_dir, true /* wipeUserdata */);
236 }
237 return binder::Status::ok();
238 }
239
disableGsiInstall(bool * _aidl_return)240 binder::Status GsiService::disableGsiInstall(bool* _aidl_return) {
241 ENFORCE_SYSTEM_OR_SHELL;
242 std::lock_guard<std::mutex> guard(main_lock_);
243
244 *_aidl_return = DisableGsiInstall();
245 return binder::Status::ok();
246 }
247
isGsiRunning(bool * _aidl_return)248 binder::Status GsiService::isGsiRunning(bool* _aidl_return) {
249 ENFORCE_SYSTEM_OR_SHELL;
250 std::lock_guard<std::mutex> guard(main_lock_);
251
252 *_aidl_return = IsGsiRunning();
253 return binder::Status::ok();
254 }
255
isGsiInstalled(bool * _aidl_return)256 binder::Status GsiService::isGsiInstalled(bool* _aidl_return) {
257 ENFORCE_SYSTEM_OR_SHELL;
258 std::lock_guard<std::mutex> guard(main_lock_);
259
260 *_aidl_return = IsGsiInstalled();
261 return binder::Status::ok();
262 }
263
isGsiInstallInProgress(bool * _aidl_return)264 binder::Status GsiService::isGsiInstallInProgress(bool* _aidl_return) {
265 ENFORCE_SYSTEM_OR_SHELL;
266 std::lock_guard<std::mutex> guard(main_lock_);
267
268 *_aidl_return = installing_;
269 return binder::Status::ok();
270 }
271
cancelGsiInstall(bool * _aidl_return)272 binder::Status GsiService::cancelGsiInstall(bool* _aidl_return) {
273 ENFORCE_SYSTEM;
274 should_abort_ = true;
275 std::lock_guard<std::mutex> guard(main_lock_);
276
277 should_abort_ = false;
278 if (installing_) {
279 PostInstallCleanup();
280 RemoveGsiFiles(install_dir_, wipe_userdata_on_failure_);
281 }
282
283 *_aidl_return = true;
284 return binder::Status::ok();
285 }
286
getGsiBootStatus(int * _aidl_return)287 binder::Status GsiService::getGsiBootStatus(int* _aidl_return) {
288 ENFORCE_SYSTEM_OR_SHELL;
289 std::lock_guard<std::mutex> guard(main_lock_);
290
291 if (!IsGsiInstalled()) {
292 *_aidl_return = BOOT_STATUS_NOT_INSTALLED;
293 return binder::Status::ok();
294 }
295
296 std::string boot_key;
297 if (!GetInstallStatus(&boot_key)) {
298 PLOG(ERROR) << "read " << kGsiInstallStatusFile;
299 *_aidl_return = BOOT_STATUS_NOT_INSTALLED;
300 return binder::Status::ok();
301 }
302
303 bool single_boot = !access(kGsiOneShotBootFile, F_OK);
304
305 if (boot_key == kInstallStatusWipe) {
306 // This overrides all other statuses.
307 *_aidl_return = BOOT_STATUS_WILL_WIPE;
308 } else if (boot_key == kInstallStatusDisabled) {
309 // A single-boot GSI will have a "disabled" status, because it's
310 // disabled immediately upon reading the one_shot_boot file. However,
311 // we still want to return SINGLE_BOOT, because it makes the
312 // transition clearer to the user.
313 if (single_boot) {
314 *_aidl_return = BOOT_STATUS_SINGLE_BOOT;
315 } else {
316 *_aidl_return = BOOT_STATUS_DISABLED;
317 }
318 } else if (single_boot) {
319 *_aidl_return = BOOT_STATUS_SINGLE_BOOT;
320 } else {
321 *_aidl_return = BOOT_STATUS_ENABLED;
322 }
323 return binder::Status::ok();
324 }
325
getUserdataImageSize(int64_t * _aidl_return)326 binder::Status GsiService::getUserdataImageSize(int64_t* _aidl_return) {
327 ENFORCE_SYSTEM;
328 std::lock_guard<std::mutex> guard(main_lock_);
329
330 *_aidl_return = -1;
331
332 if (installing_) {
333 // Size has already been computed.
334 *_aidl_return = userdata_size_;
335 } else if (IsGsiRunning()) {
336 // :TODO: libdm
337 unique_fd fd(open(kUserdataDevice, O_RDONLY | O_NOFOLLOW | O_CLOEXEC));
338 if (fd < 0) {
339 PLOG(ERROR) << "open " << kUserdataDevice;
340 return binder::Status::ok();
341 }
342
343 int64_t size;
344 if (ioctl(fd, BLKGETSIZE64, &size)) {
345 PLOG(ERROR) << "BLKGETSIZE64 " << kUserdataDevice;
346 return binder::Status::ok();
347 }
348 *_aidl_return = size;
349 } else {
350 // Stat the size of the userdata file.
351 auto userdata_gsi = GetInstalledImagePath("userdata_gsi");
352 struct stat s;
353 if (stat(userdata_gsi.c_str(), &s)) {
354 if (errno != ENOENT) {
355 PLOG(ERROR) << "open " << userdata_gsi;
356 return binder::Status::ok();
357 }
358 *_aidl_return = 0;
359 } else {
360 *_aidl_return = s.st_size;
361 }
362 }
363 return binder::Status::ok();
364 }
365
getInstalledGsiImageDir(std::string * _aidl_return)366 binder::Status GsiService::getInstalledGsiImageDir(std::string* _aidl_return) {
367 ENFORCE_SYSTEM;
368 std::lock_guard<std::mutex> guard(main_lock_);
369
370 if (IsGsiInstalled()) {
371 *_aidl_return = GetInstalledImageDir();
372 }
373 return binder::Status::ok();
374 }
375
wipeGsiUserdata(int * _aidl_return)376 binder::Status GsiService::wipeGsiUserdata(int* _aidl_return) {
377 ENFORCE_SYSTEM_OR_SHELL;
378 std::lock_guard<std::mutex> guard(main_lock_);
379
380 if (IsGsiRunning() || !IsGsiInstalled()) {
381 *_aidl_return = IGsiService::INSTALL_ERROR_GENERIC;
382 return binder::Status::ok();
383 }
384
385 *_aidl_return = WipeUserdata();
386
387 return binder::Status::ok();
388 }
389
CheckUid(AccessLevel level)390 binder::Status GsiService::CheckUid(AccessLevel level) {
391 std::vector<uid_t> allowed_uids{AID_ROOT, AID_SYSTEM};
392 if (level == AccessLevel::SystemOrShell) {
393 allowed_uids.push_back(AID_SHELL);
394 }
395
396 uid_t uid = IPCThreadState::self()->getCallingUid();
397 for (const auto& allowed_uid : allowed_uids) {
398 if (allowed_uid == uid) {
399 return binder::Status::ok();
400 }
401 }
402
403 auto message = StringPrintf("UID %d is not allowed", uid);
404 return binder::Status::fromExceptionCode(binder::Status::EX_SECURITY,
405 String8(message.c_str()));
406 }
407
PostInstallCleanup()408 void GsiService::PostInstallCleanup() {
409 // This must be closed before unmapping partitions.
410 system_writer_ = nullptr;
411
412 const auto& dm = DeviceMapper::Instance();
413 if (dm.GetState("userdata_gsi") != DmDeviceState::INVALID) {
414 DestroyLogicalPartition("userdata_gsi", kDmTimeout);
415 }
416 if (dm.GetState("system_gsi") != DmDeviceState::INVALID) {
417 DestroyLogicalPartition("system_gsi", kDmTimeout);
418 }
419
420 installing_ = false;
421 partitions_ .clear();
422 }
423
IsExternalStoragePath(const std::string & path)424 static bool IsExternalStoragePath(const std::string& path) {
425 if (!android::base::StartsWith(path, "/mnt/media_rw/")) {
426 return false;
427 }
428 unique_fd fd(open(path.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW));
429 if (fd < 0) {
430 PLOG(ERROR) << "open failed: " << path;
431 return false;
432 }
433 struct statfs info;
434 if (fstatfs(fd, &info)) {
435 PLOG(ERROR) << "statfs failed: " << path;
436 return false;
437 }
438 LOG(ERROR) << "fs type: " << info.f_type;
439 return info.f_type == MSDOS_SUPER_MAGIC;
440 }
441
ValidateInstallParams(GsiInstallParams * params)442 int GsiService::ValidateInstallParams(GsiInstallParams* params) {
443 // If no install path was specified, use the default path. We also allow
444 // specifying the top-level folder, and then we choose the correct location
445 // underneath.
446 if (params->installDir.empty() || params->installDir == "/data/gsi") {
447 params->installDir = kDefaultGsiImageFolder;
448 }
449
450 // Normalize the path and add a trailing slash.
451 std::string origInstallDir = params->installDir;
452 if (!android::base::Realpath(origInstallDir, ¶ms->installDir)) {
453 PLOG(ERROR) << "realpath failed: " << origInstallDir;
454 return INSTALL_ERROR_GENERIC;
455 }
456 // Ensure the path ends in / for consistency. Even though GetImagePath()
457 // does this already, we want it to appear this way in install_dir.
458 if (!android::base::EndsWith(params->installDir, "/")) {
459 params->installDir += "/";
460 }
461
462 // Currently, we can only install to /data/gsi/ or external storage.
463 if (IsExternalStoragePath(params->installDir)) {
464 Fstab fstab;
465 if (!ReadDefaultFstab(&fstab)) {
466 LOG(ERROR) << "cannot read default fstab";
467 return INSTALL_ERROR_GENERIC;
468 }
469 FstabEntry* system = GetEntryForMountPoint(&fstab, "/system");
470 if (!system) {
471 LOG(ERROR) << "cannot find /system fstab entry";
472 return INSTALL_ERROR_GENERIC;
473 }
474 if (fs_mgr_verity_is_check_at_most_once(*system)) {
475 LOG(ERROR) << "cannot install GSIs to external media if verity uses check_at_most_once";
476 return INSTALL_ERROR_GENERIC;
477 }
478 } else if (params->installDir != kDefaultGsiImageFolder) {
479 LOG(ERROR) << "cannot install GSI to " << params->installDir;
480 return INSTALL_ERROR_GENERIC;
481 }
482
483 if (params->gsiSize % LP_SECTOR_SIZE) {
484 LOG(ERROR) << "GSI size " << params->gsiSize << " is not a multiple of " << LP_SECTOR_SIZE;
485 return INSTALL_ERROR_GENERIC;
486 }
487 if (params->userdataSize % LP_SECTOR_SIZE) {
488 LOG(ERROR) << "userdata size " << params->userdataSize << " is not a multiple of "
489 << LP_SECTOR_SIZE;
490 return INSTALL_ERROR_GENERIC;
491 }
492 return INSTALL_OK;
493 }
494
StartInstall(const GsiInstallParams & params)495 int GsiService::StartInstall(const GsiInstallParams& params) {
496 installing_ = true;
497 userdata_block_size_ = 0;
498 system_block_size_ = 0;
499 gsi_size_ = params.gsiSize;
500 userdata_size_ = (params.userdataSize) ? params.userdataSize : kDefaultUserdataSize;
501 wipe_userdata_ = params.wipeUserdata;
502 can_use_devicemapper_ = false;
503 gsi_bytes_written_ = 0;
504 install_dir_ = params.installDir;
505
506 userdata_gsi_path_ = GetImagePath(install_dir_, "userdata_gsi");
507 system_gsi_path_ = GetImagePath(install_dir_, "system_gsi");
508
509 // Only rm userdata_gsi if one didn't already exist.
510 wipe_userdata_on_failure_ = wipe_userdata_ || access(userdata_gsi_path_.c_str(), F_OK);
511
512 if (int status = PerformSanityChecks()) {
513 return status;
514 }
515 if (int status = PreallocateFiles()) {
516 return status;
517 }
518 if (int status = DetermineReadWriteMethod()) {
519 return status;
520 }
521 if (!FormatUserdata()) {
522 return INSTALL_ERROR_GENERIC;
523 }
524
525 // Map system_gsi so we can write to it.
526 system_writer_ = OpenPartition("system_gsi");
527 if (!system_writer_) {
528 return INSTALL_ERROR_GENERIC;
529 }
530 return INSTALL_OK;
531 }
532
DetermineReadWriteMethod()533 int GsiService::DetermineReadWriteMethod() {
534 // If there is a device-mapper node wrapping the block device, then we're
535 // able to create another node around it; the dm layer does not carry the
536 // exclusion lock down the stack when a mount occurs.
537 //
538 // If there is no intermediate device-mapper node, then partitions cannot be
539 // opened writable due to sepolicy and exclusivity of having a mounted
540 // filesystem. This should only happen on devices with no encryption, or
541 // devices with FBE and no metadata encryption. For these cases it suffices
542 // to perform normal file writes to /data/gsi (which is unencrypted).
543 std::string block_device;
544 if (!FiemapWriter::GetBlockDeviceForFile(system_gsi_path_.c_str(), &block_device,
545 &can_use_devicemapper_)) {
546 return INSTALL_ERROR_GENERIC;
547 }
548 if (install_dir_ != kDefaultGsiImageFolder && can_use_devicemapper_) {
549 // Never use device-mapper on external media. We don't support adopted
550 // storage yet, and accidentally using device-mapper could be dangerous
551 // as we hardcode the userdata device as backing storage.
552 LOG(ERROR) << "unexpected device-mapper node used to mount external media";
553 return INSTALL_ERROR_GENERIC;
554 }
555 return INSTALL_OK;
556 }
557
GetImagePath(const std::string & image_dir,const std::string & name)558 std::string GsiService::GetImagePath(const std::string& image_dir, const std::string& name) {
559 std::string dir = image_dir;
560 if (!android::base::EndsWith(dir, "/")) {
561 dir += "/";
562 }
563 return dir + name + ".img";
564 }
565
GetInstalledImageDir()566 std::string GsiService::GetInstalledImageDir() {
567 // If there's no install left, just return /data/gsi since that's where
568 // installs go by default.
569 std::string dir;
570 if (android::base::ReadFileToString(kGsiInstallDirFile, &dir)) {
571 return dir;
572 }
573 return kDefaultGsiImageFolder;
574 }
575
GetInstalledImagePath(const std::string & name)576 std::string GsiService::GetInstalledImagePath(const std::string& name) {
577 return GetImagePath(GetInstalledImageDir(), name);
578 }
579
PerformSanityChecks()580 int GsiService::PerformSanityChecks() {
581 if (gsi_size_ < 0) {
582 LOG(ERROR) << "image size " << gsi_size_ << " is negative";
583 return INSTALL_ERROR_GENERIC;
584 }
585 if (android::gsi::IsGsiRunning()) {
586 LOG(ERROR) << "cannot install gsi inside a live gsi";
587 return INSTALL_ERROR_GENERIC;
588 }
589
590 struct statvfs sb;
591 if (statvfs(install_dir_.c_str(), &sb)) {
592 PLOG(ERROR) << "failed to read file system stats";
593 return INSTALL_ERROR_GENERIC;
594 }
595
596 // This is the same as android::vold::GetFreebytes() but we also
597 // need the total file system size so we open code it here.
598 uint64_t free_space = 1ULL * sb.f_bavail * sb.f_frsize;
599 uint64_t fs_size = sb.f_blocks * sb.f_frsize;
600 if (free_space <= (gsi_size_ + userdata_size_)) {
601 LOG(ERROR) << "not enough free space (only " << free_space << " bytes available)";
602 return INSTALL_ERROR_NO_SPACE;
603 }
604 // We are asking for 40% of the /data to be empty.
605 // TODO: may be not hard code it like this
606 double free_space_percent = ((1.0 * free_space) / fs_size) * 100;
607 if (free_space_percent < kMinimumFreeSpaceThreshold) {
608 LOG(ERROR) << "free space " << static_cast<uint64_t>(free_space_percent)
609 << "% is below the minimum threshold of " << kMinimumFreeSpaceThreshold << "%";
610 return INSTALL_ERROR_FILE_SYSTEM_CLUTTERED;
611 }
612 return INSTALL_OK;
613 }
614
PreallocateFiles()615 int GsiService::PreallocateFiles() {
616 if (wipe_userdata_) {
617 SplitFiemap::RemoveSplitFiles(userdata_gsi_path_);
618 }
619 SplitFiemap::RemoveSplitFiles(system_gsi_path_);
620
621 // TODO: trigger GC from fiemap writer.
622
623 // Create fallocated files.
624 if (int status = PreallocateUserdata()) {
625 return status;
626 }
627 if (int status = PreallocateSystem()) {
628 return status;
629 }
630
631 // Save the extent information in liblp.
632 metadata_ = CreateMetadata();
633 if (!metadata_) {
634 return INSTALL_ERROR_GENERIC;
635 }
636
637 UpdateProgress(STATUS_COMPLETE, 0);
638 return INSTALL_OK;
639 }
640
PreallocateUserdata()641 int GsiService::PreallocateUserdata() {
642 int error;
643 std::unique_ptr<SplitFiemap> userdata_image;
644 if (wipe_userdata_ || access(userdata_gsi_path_.c_str(), F_OK)) {
645 StartAsyncOperation("create userdata", userdata_size_);
646 userdata_image = CreateFiemapWriter(userdata_gsi_path_, userdata_size_, &error);
647 if (!userdata_image) {
648 LOG(ERROR) << "Could not create userdata image: " << userdata_gsi_path_;
649 return error;
650 }
651 // Signal that we need to reformat userdata.
652 wipe_userdata_ = true;
653 } else {
654 userdata_image = CreateFiemapWriter(userdata_gsi_path_, 0, &error);
655 if (!userdata_image) {
656 LOG(ERROR) << "Could not open userdata image: " << userdata_gsi_path_;
657 return error;
658 }
659 if (userdata_size_ && userdata_image->size() < userdata_size_) {
660 // :TODO: need to fallocate more blocks and resizefs.
661 }
662 userdata_size_ = userdata_image->size();
663 }
664
665 userdata_block_size_ = userdata_image->block_size();
666
667 Image image = {
668 .writer = std::move(userdata_image),
669 .actual_size = userdata_size_,
670 };
671 partitions_.emplace(std::make_pair("userdata_gsi", std::move(image)));
672 return INSTALL_OK;
673 }
674
PreallocateSystem()675 int GsiService::PreallocateSystem() {
676 StartAsyncOperation("create system", gsi_size_);
677
678 int error;
679 auto system_image = CreateFiemapWriter(system_gsi_path_, gsi_size_, &error);
680 if (!system_image) {
681 return error;
682 }
683
684 system_block_size_ = system_image->block_size();
685
686 Image image = {
687 .writer = std::move(system_image),
688 .actual_size = gsi_size_,
689 };
690 partitions_.emplace(std::make_pair("system_gsi", std::move(image)));
691 return INSTALL_OK;
692 }
693
CreateFiemapWriter(const std::string & path,uint64_t size,int * error)694 std::unique_ptr<SplitFiemap> GsiService::CreateFiemapWriter(const std::string& path,
695 uint64_t size, int* error) {
696 bool create = (size != 0);
697
698 std::function<bool(uint64_t, uint64_t)> progress;
699 if (create) {
700 // TODO: allow cancelling inside cancelGsiInstall.
701 progress = [this](uint64_t bytes, uint64_t /* total */) -> bool {
702 UpdateProgress(STATUS_WORKING, bytes);
703 if (should_abort_) return false;
704 return true;
705 };
706 }
707
708 std::unique_ptr<SplitFiemap> file;
709 if (!size) {
710 file = SplitFiemap::Open(path);
711 } else {
712 file = SplitFiemap::Create(path, size, 0, std::move(progress));
713 }
714 if (!file) {
715 LOG(ERROR) << "failed to create or open " << path;
716 *error = INSTALL_ERROR_GENERIC;
717 return nullptr;
718 }
719
720 uint64_t extents = file->extents().size();
721 if (extents > kMaximumExtents) {
722 LOG(ERROR) << "file " << path << " has too many extents: " << extents;
723 *error = INSTALL_ERROR_FILE_SYSTEM_CLUTTERED;
724 return nullptr;
725 }
726 return file;
727 }
728
729 // Write data through an fd.
730 class FdWriter final : public GsiService::WriteHelper {
731 public:
FdWriter(const std::string & path,unique_fd && fd)732 FdWriter(const std::string& path, unique_fd&& fd) : path_(path), fd_(std::move(fd)) {}
733
Write(const void * data,uint64_t bytes)734 bool Write(const void* data, uint64_t bytes) override {
735 return android::base::WriteFully(fd_, data, bytes);
736 }
Flush()737 bool Flush() override {
738 if (fsync(fd_)) {
739 PLOG(ERROR) << "fsync failed: " << path_;
740 return false;
741 }
742 return true;
743 }
Size()744 uint64_t Size() override { return get_block_device_size(fd_); }
745
746 private:
747 std::string path_;
748 unique_fd fd_;
749 };
750
751 // Write data through a SplitFiemap.
752 class SplitFiemapWriter final : public GsiService::WriteHelper {
753 public:
SplitFiemapWriter(SplitFiemap * writer)754 explicit SplitFiemapWriter(SplitFiemap* writer) : writer_(writer) {}
755
Write(const void * data,uint64_t bytes)756 bool Write(const void* data, uint64_t bytes) override {
757 return writer_->Write(data, bytes);
758 }
Flush()759 bool Flush() override {
760 return writer_->Flush();
761 }
Size()762 uint64_t Size() override { return writer_->size(); }
763
764 private:
765 SplitFiemap* writer_;
766 };
767
OpenPartition(const std::string & name)768 std::unique_ptr<GsiService::WriteHelper> GsiService::OpenPartition(const std::string& name) {
769 if (can_use_devicemapper_) {
770 std::string path;
771 if (!CreateLogicalPartition(kUserdataDevice, *metadata_.get(), name, true, kDmTimeout,
772 &path)) {
773 LOG(ERROR) << "Error creating device-mapper node for " << name;
774 return {};
775 }
776
777 static const int kOpenFlags = O_RDWR | O_NOFOLLOW | O_CLOEXEC;
778 unique_fd fd(open(path.c_str(), kOpenFlags));
779 if (fd < 0) {
780 PLOG(ERROR) << "could not open " << path;
781 }
782 return std::make_unique<FdWriter>(GetImagePath(install_dir_, name), std::move(fd));
783 }
784
785 auto iter = partitions_.find(name);
786 if (iter == partitions_.end()) {
787 LOG(ERROR) << "could not find partition " << name;
788 return {};
789 }
790 return std::make_unique<SplitFiemapWriter>(iter->second.writer.get());
791 }
792
CommitGsiChunk(int stream_fd,int64_t bytes)793 bool GsiService::CommitGsiChunk(int stream_fd, int64_t bytes) {
794 StartAsyncOperation("write gsi", gsi_size_);
795
796 if (bytes < 0) {
797 LOG(ERROR) << "chunk size " << bytes << " is negative";
798 return false;
799 }
800
801 auto buffer = std::make_unique<char[]>(system_block_size_);
802
803 int progress = -1;
804 uint64_t remaining = bytes;
805 while (remaining) {
806 // :TODO: check file pin status!
807 size_t max_to_read = std::min(system_block_size_, remaining);
808 ssize_t rv = TEMP_FAILURE_RETRY(read(stream_fd, buffer.get(), max_to_read));
809 if (rv < 0) {
810 PLOG(ERROR) << "read gsi chunk";
811 return false;
812 }
813 if (rv == 0) {
814 LOG(ERROR) << "no bytes left in stream";
815 return false;
816 }
817 if (!CommitGsiChunk(buffer.get(), rv)) {
818 return false;
819 }
820 CHECK(static_cast<uint64_t>(rv) <= remaining);
821 remaining -= rv;
822
823 // Only update the progress when the % (or permille, in this case)
824 // significantly changes.
825 int new_progress = ((gsi_size_ - remaining) * 1000) / gsi_size_;
826 if (new_progress != progress) {
827 UpdateProgress(STATUS_WORKING, gsi_size_ - remaining);
828 }
829 }
830
831 UpdateProgress(STATUS_COMPLETE, gsi_size_);
832 return true;
833 }
834
CommitGsiChunk(const void * data,size_t bytes)835 bool GsiService::CommitGsiChunk(const void* data, size_t bytes) {
836 if (!installing_) {
837 LOG(ERROR) << "no gsi installation in progress";
838 return false;
839 }
840 if (static_cast<uint64_t>(bytes) > gsi_size_ - gsi_bytes_written_) {
841 // We cannot write past the end of the image file.
842 LOG(ERROR) << "chunk size " << bytes << " exceeds remaining image size (" << gsi_size_
843 << " expected, " << gsi_bytes_written_ << " written)";
844 return false;
845 }
846
847 if (!system_writer_->Write(data, bytes)) {
848 PLOG(ERROR) << "write failed";
849 return false;
850 }
851 gsi_bytes_written_ += bytes;
852 return true;
853 }
854
SetGsiBootable(bool one_shot)855 int GsiService::SetGsiBootable(bool one_shot) {
856 if (gsi_bytes_written_ != gsi_size_) {
857 // We cannot boot if the image is incomplete.
858 LOG(ERROR) << "image incomplete; expected " << gsi_size_ << " bytes, waiting for "
859 << (gsi_size_ - gsi_bytes_written_) << " bytes";
860 return INSTALL_ERROR_GENERIC;
861 }
862
863 if (!system_writer_->Flush()) {
864 return INSTALL_ERROR_GENERIC;
865 }
866
867 // If files moved (are no longer pinned), the metadata file will be invalid.
868 for (const auto& [name, image] : partitions_) {
869 if (!image.writer->HasPinnedExtents()) {
870 LOG(ERROR) << name << " no longer has pinned extents";
871 return INSTALL_ERROR_GENERIC;
872 }
873 }
874
875 // Remember the installation directory.
876 if (!android::base::WriteStringToFile(install_dir_, kGsiInstallDirFile)) {
877 PLOG(ERROR) << "write failed: " << kGsiInstallDirFile;
878 return INSTALL_ERROR_GENERIC;
879 }
880
881 // Note: create the install status file last, since this is the actual boot
882 // indicator.
883 if (!CreateMetadataFile() || !SetBootMode(one_shot) || !CreateInstallStatusFile()) {
884 return INSTALL_ERROR_GENERIC;
885 }
886 return INSTALL_OK;
887 }
888
ReenableGsi(bool one_shot)889 int GsiService::ReenableGsi(bool one_shot) {
890 if (!android::gsi::IsGsiInstalled()) {
891 LOG(ERROR) << "no gsi installed - cannot re-enable";
892 return INSTALL_ERROR_GENERIC;
893 }
894
895 std::string boot_key;
896 if (!GetInstallStatus(&boot_key)) {
897 PLOG(ERROR) << "read " << kGsiInstallStatusFile;
898 return INSTALL_ERROR_GENERIC;
899 }
900 if (boot_key != kInstallStatusDisabled) {
901 LOG(ERROR) << "GSI is not currently disabled";
902 return INSTALL_ERROR_GENERIC;
903 }
904
905 if (IsGsiRunning()) {
906 if (!SetBootMode(one_shot) || !CreateInstallStatusFile()) {
907 return INSTALL_ERROR_GENERIC;
908 }
909 return INSTALL_OK;
910 }
911 // Note: this metadata is only used to recover the original partition sizes.
912 // We do not trust the extent information, which will get rebuilt later.
913 auto old_metadata = ReadFromImageFile(kGsiLpMetadataFile);
914 if (!old_metadata) {
915 LOG(ERROR) << "GSI install is incomplete";
916 return INSTALL_ERROR_GENERIC;
917 }
918
919 // Set up enough installer state so that we can use various helper
920 // methods.
921 //
922 // TODO(dvander) Extract all of the installer state into a separate
923 // class so this is more manageable.
924 install_dir_ = GetInstalledImageDir();
925 system_gsi_path_ = GetImagePath(install_dir_, "system_gsi");
926 if (int error = DetermineReadWriteMethod()) {
927 return error;
928 }
929
930 // Recover parition information.
931 Image userdata_image;
932 if (int error = GetExistingImage(*old_metadata.get(), "userdata_gsi", &userdata_image)) {
933 return error;
934 }
935 partitions_.emplace(std::make_pair("userdata_gsi", std::move(userdata_image)));
936
937 Image system_image;
938 if (int error = GetExistingImage(*old_metadata.get(), "system_gsi", &system_image)) {
939 return error;
940 }
941 partitions_.emplace(std::make_pair("system_gsi", std::move(system_image)));
942
943 metadata_ = CreateMetadata();
944 if (!metadata_) {
945 return INSTALL_ERROR_GENERIC;
946 }
947 if (!CreateMetadataFile() || !SetBootMode(one_shot) || !CreateInstallStatusFile()) {
948 return INSTALL_ERROR_GENERIC;
949 }
950 return INSTALL_OK;
951 }
952
WipeUserdata()953 int GsiService::WipeUserdata() {
954 // Note: this metadata is only used to recover the original partition sizes.
955 // We do not trust the extent information, which will get rebuilt later.
956 auto old_metadata = ReadFromImageFile(kGsiLpMetadataFile);
957 if (!old_metadata) {
958 LOG(ERROR) << "GSI install is incomplete";
959 return INSTALL_ERROR_GENERIC;
960 }
961
962 install_dir_ = GetInstalledImageDir();
963 system_gsi_path_ = GetImagePath(install_dir_, "system_gsi");
964 if (int error = DetermineReadWriteMethod()) {
965 return error;
966 }
967
968 // Recover parition information.
969 Image userdata_image;
970 if (int error = GetExistingImage(*old_metadata.get(), "userdata_gsi", &userdata_image)) {
971 return error;
972 }
973 partitions_.emplace(std::make_pair("userdata_gsi", std::move(userdata_image)));
974
975 metadata_ = CreateMetadata();
976 if (!metadata_) {
977 return INSTALL_ERROR_GENERIC;
978 }
979
980 auto writer = OpenPartition("userdata_gsi");
981 if (!writer) {
982 return IGsiService::INSTALL_ERROR_GENERIC;
983 }
984
985 // Wipe the first 1MiB of the device, ensuring both the first block and
986 // the superblock are destroyed.
987 static constexpr uint64_t kEraseSize = 1024 * 1024;
988
989 std::string zeroes(4096, 0);
990 uint64_t erase_size = std::min(kEraseSize, writer->Size());
991 for (uint64_t i = 0; i < erase_size; i += zeroes.size()) {
992 if (!writer->Write(zeroes.data(), zeroes.size())) {
993 PLOG(ERROR) << "write userdata_gsi";
994 return IGsiService::INSTALL_ERROR_GENERIC;
995 }
996 }
997 return INSTALL_OK;
998 }
999
GetPartitionSize(const LpMetadata & metadata,const LpMetadataPartition & partition)1000 static uint64_t GetPartitionSize(const LpMetadata& metadata, const LpMetadataPartition& partition) {
1001 uint64_t total = 0;
1002 for (size_t i = 0; i < partition.num_extents; i++) {
1003 const auto& extent = metadata.extents[partition.first_extent_index + i];
1004 if (extent.target_type != LP_TARGET_TYPE_LINEAR) {
1005 LOG(ERROR) << "non-linear extent detected";
1006 return 0;
1007 }
1008 total += extent.num_sectors * LP_SECTOR_SIZE;
1009 }
1010 return total;
1011 }
1012
GetPartitionSize(const LpMetadata & metadata,const std::string & name)1013 static uint64_t GetPartitionSize(const LpMetadata& metadata, const std::string& name) {
1014 for (const auto& partition : metadata.partitions) {
1015 if (GetPartitionName(partition) == name) {
1016 return GetPartitionSize(metadata, partition);
1017 }
1018 }
1019 return 0;
1020 }
1021
GetExistingImage(const LpMetadata & metadata,const std::string & name,Image * image)1022 int GsiService::GetExistingImage(const LpMetadata& metadata, const std::string& name,
1023 Image* image) {
1024 int error;
1025 std::string path = GetInstalledImagePath(name);
1026 auto writer = CreateFiemapWriter(path.c_str(), 0, &error);
1027 if (!writer) {
1028 return error;
1029 }
1030
1031 // Even after recovering the FIEMAP, we also need to know the exact intended
1032 // size of the image, since FiemapWriter may have extended the final block.
1033 uint64_t actual_size = GetPartitionSize(metadata, name);
1034 if (!actual_size) {
1035 LOG(ERROR) << "Could not determine the pre-existing size of " << name;
1036 return INSTALL_ERROR_GENERIC;
1037 }
1038 image->writer = std::move(writer);
1039 image->actual_size = actual_size;
1040 return INSTALL_OK;
1041 }
1042
RemoveGsiFiles(const std::string & install_dir,bool wipeUserdata)1043 bool GsiService::RemoveGsiFiles(const std::string& install_dir, bool wipeUserdata) {
1044 bool ok = true;
1045 std::string message;
1046 if (!SplitFiemap::RemoveSplitFiles(GetImagePath(install_dir, "system_gsi"), &message)) {
1047 LOG(ERROR) << message;
1048 ok = false;
1049 }
1050 if (wipeUserdata &&
1051 !SplitFiemap::RemoveSplitFiles(GetImagePath(install_dir, "userdata_gsi"), &message)) {
1052 LOG(ERROR) << message;
1053 ok = false;
1054 }
1055
1056 std::vector<std::string> files{
1057 kGsiInstallStatusFile,
1058 kGsiLpMetadataFile,
1059 kGsiOneShotBootFile,
1060 kGsiInstallDirFile,
1061 };
1062 for (const auto& file : files) {
1063 if (!android::base::RemoveFileIfExists(file, &message)) {
1064 LOG(ERROR) << message;
1065 ok = false;
1066 }
1067 }
1068 return ok;
1069 }
1070
DisableGsiInstall()1071 bool GsiService::DisableGsiInstall() {
1072 if (!android::gsi::IsGsiInstalled()) {
1073 LOG(ERROR) << "cannot disable gsi install - no install detected";
1074 return false;
1075 }
1076 if (installing_) {
1077 LOG(ERROR) << "cannot disable gsi during GSI installation";
1078 return false;
1079 }
1080 if (!DisableGsi()) {
1081 PLOG(ERROR) << "could not write gsi status";
1082 return false;
1083 }
1084 return true;
1085 }
1086
CreateMetadata()1087 std::unique_ptr<LpMetadata> GsiService::CreateMetadata() {
1088 std::string data_device_path;
1089 if (install_dir_ == kDefaultGsiImageFolder && !access(kUserdataDevice, F_OK)) {
1090 data_device_path = kUserdataDevice;
1091 } else {
1092 auto writer = partitions_["system_gsi"].writer.get();
1093 data_device_path = writer->bdev_path();
1094 }
1095 auto data_device_name = android::base::Basename(data_device_path);
1096
1097 PartitionOpener opener;
1098 BlockDeviceInfo data_device_info;
1099 if (!opener.GetInfo(data_device_path, &data_device_info)) {
1100 LOG(ERROR) << "Error reading userdata partition";
1101 return nullptr;
1102 }
1103
1104 std::vector<BlockDeviceInfo> block_devices = {data_device_info};
1105 auto builder = MetadataBuilder::New(block_devices, data_device_name, 128 * 1024, 1);
1106 if (!builder) {
1107 LOG(ERROR) << "Error creating metadata builder";
1108 return nullptr;
1109 }
1110 builder->IgnoreSlotSuffixing();
1111
1112 for (const auto& [name, image] : partitions_) {
1113 uint32_t flags = LP_PARTITION_ATTR_NONE;
1114 if (name == "system_gsi") {
1115 flags |= LP_PARTITION_ATTR_READONLY;
1116 }
1117 Partition* partition = builder->AddPartition(name, flags);
1118 if (!partition) {
1119 LOG(ERROR) << "Error adding " << name << " to partition table";
1120 return nullptr;
1121 }
1122 if (!AddPartitionFiemap(builder.get(), partition, image, data_device_name)) {
1123 return nullptr;
1124 }
1125 }
1126
1127 auto metadata = builder->Export();
1128 if (!metadata) {
1129 LOG(ERROR) << "Error exporting partition table";
1130 return nullptr;
1131 }
1132 return metadata;
1133 }
1134
CreateMetadataFile()1135 bool GsiService::CreateMetadataFile() {
1136 if (!WriteToImageFile(kGsiLpMetadataFile, *metadata_.get())) {
1137 LOG(ERROR) << "Error writing GSI partition table image";
1138 return false;
1139 }
1140 return true;
1141 }
1142
FormatUserdata()1143 bool GsiService::FormatUserdata() {
1144 auto writer = OpenPartition("userdata_gsi");
1145 if (!writer) {
1146 return false;
1147 }
1148
1149 // libcutils checks the first 4K, no matter the block size.
1150 std::string zeroes(4096, 0);
1151 if (!writer->Write(zeroes.data(), zeroes.size())) {
1152 PLOG(ERROR) << "write userdata_gsi";
1153 return false;
1154 }
1155 return true;
1156 }
1157
AddPartitionFiemap(MetadataBuilder * builder,Partition * partition,const Image & image,const std::string & block_device)1158 bool GsiService::AddPartitionFiemap(MetadataBuilder* builder, Partition* partition,
1159 const Image& image, const std::string& block_device) {
1160 uint64_t sectors_needed = image.actual_size / LP_SECTOR_SIZE;
1161 for (const auto& extent : image.writer->extents()) {
1162 // :TODO: block size check for length, not sector size
1163 if (extent.fe_length % LP_SECTOR_SIZE != 0) {
1164 LOG(ERROR) << "Extent is not sector-aligned: " << extent.fe_length;
1165 return false;
1166 }
1167 if (extent.fe_physical % LP_SECTOR_SIZE != 0) {
1168 LOG(ERROR) << "Extent physical sector is not sector-aligned: " << extent.fe_physical;
1169 return false;
1170 }
1171
1172 uint64_t num_sectors =
1173 std::min(static_cast<uint64_t>(extent.fe_length / LP_SECTOR_SIZE), sectors_needed);
1174 if (!num_sectors || !sectors_needed) {
1175 // This should never happen, but we include it just in case. It would
1176 // indicate that the last filesystem block had multiple extents.
1177 LOG(WARNING) << "FiemapWriter allocated extra blocks";
1178 break;
1179 }
1180
1181 uint64_t physical_sector = extent.fe_physical / LP_SECTOR_SIZE;
1182 if (!builder->AddLinearExtent(partition, block_device, num_sectors, physical_sector)) {
1183 LOG(ERROR) << "Could not add extent to lp metadata";
1184 return false;
1185 }
1186
1187 sectors_needed -= num_sectors;
1188 }
1189 return true;
1190 }
1191
SetBootMode(bool one_shot)1192 bool GsiService::SetBootMode(bool one_shot) {
1193 if (one_shot) {
1194 if (!android::base::WriteStringToFile("1", kGsiOneShotBootFile)) {
1195 PLOG(ERROR) << "write " << kGsiOneShotBootFile;
1196 return false;
1197 }
1198 } else if (!access(kGsiOneShotBootFile, F_OK)) {
1199 std::string error;
1200 if (!android::base::RemoveFileIfExists(kGsiOneShotBootFile, &error)) {
1201 LOG(ERROR) << error;
1202 return false;
1203 }
1204 }
1205 return true;
1206 }
1207
CreateInstallStatusFile()1208 bool GsiService::CreateInstallStatusFile() {
1209 if (!android::base::WriteStringToFile("0", kGsiInstallStatusFile)) {
1210 PLOG(ERROR) << "write " << kGsiInstallStatusFile;
1211 return false;
1212 }
1213 return true;
1214 }
1215
RunStartupTasks()1216 void GsiService::RunStartupTasks() {
1217 if (!IsGsiInstalled()) {
1218 return;
1219 }
1220
1221 std::string boot_key;
1222 if (!GetInstallStatus(&boot_key)) {
1223 PLOG(ERROR) << "read " << kGsiInstallStatusFile;
1224 return;
1225 }
1226
1227 if (!IsGsiRunning()) {
1228 // Check if a wipe was requested from fastboot or adb-in-gsi.
1229 if (boot_key == kInstallStatusWipe) {
1230 RemoveGsiFiles(GetInstalledImageDir(), true /* wipeUserdata */);
1231 }
1232 } else {
1233 // NB: When single-boot is enabled, init will write "disabled" into the
1234 // install_status file, which will cause GetBootAttempts to return
1235 // false. Thus, we won't write "ok" here.
1236 int ignore;
1237 if (GetBootAttempts(boot_key, &ignore)) {
1238 // Mark the GSI as having successfully booted.
1239 if (!android::base::WriteStringToFile(kInstallStatusOk, kGsiInstallStatusFile)) {
1240 PLOG(ERROR) << "write " << kGsiInstallStatusFile;
1241 }
1242 }
1243 }
1244 }
1245
1246 } // namespace gsi
1247 } // namespace android
1248