blob: fdb147e1480c42218e5e2afb65ac8870296f13dc [file] [log] [blame]
David Andersonee84d742019-01-07 18:10:29 -08001//
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 "libgsi/libgsi.h"
18
David Andersonb3aff182019-01-11 14:37:51 -080019#include <string.h>
David Andersonee84d742019-01-07 18:10:29 -080020#include <unistd.h>
21
David Andersonb3aff182019-01-11 14:37:51 -080022#include <string>
23
David Andersonee84d742019-01-07 18:10:29 -080024#include <android-base/file.h>
David Andersonb3aff182019-01-11 14:37:51 -080025#include <android-base/parseint.h>
David Anderson15d8b4c2019-02-01 16:30:31 -080026#include <android-base/unique_fd.h>
David Andersonee84d742019-01-07 18:10:29 -080027
28#include "file_paths.h"
David Andersonb3aff182019-01-11 14:37:51 -080029#include "libgsi_private.h"
David Andersonee84d742019-01-07 18:10:29 -080030
31namespace android {
32namespace gsi {
33
David Andersonb3aff182019-01-11 14:37:51 -080034using namespace std::literals;
David Anderson15d8b4c2019-02-01 16:30:31 -080035using android::base::unique_fd;
David Andersonb3aff182019-01-11 14:37:51 -080036
David Andersonee84d742019-01-07 18:10:29 -080037bool IsGsiRunning() {
38 return !access(kGsiBootedIndicatorFile, F_OK);
39}
40
41bool IsGsiInstalled() {
David Andersonb3aff182019-01-11 14:37:51 -080042 return !access(kGsiInstallStatusFile, F_OK);
David Andersonee84d742019-01-07 18:10:29 -080043}
44
David Anderson782b3232019-01-17 14:18:20 -080045// If true, we only ever allow a single boot into GSI. Rebooting the device
46// will bring the device back to its normal system image.
47static constexpr bool kOnlyAllowSingleBoot = true;
48
David Anderson15d8b4c2019-02-01 16:30:31 -080049static bool WriteAndSyncFile(const std::string& data, const std::string& file) {
50 unique_fd fd(open(file.c_str(), O_WRONLY | O_NOFOLLOW | O_CLOEXEC));
51 if (fd < 0) {
52 return false;
53 }
54 if (!android::base::WriteFully(fd, data.c_str(), data.size())) {
55 return false;
56 }
57 return fsync(fd) == 0;
58}
59
David Andersonee84d742019-01-07 18:10:29 -080060static bool CanBootIntoGsi(std::string* error) {
David Andersonc053b3b2019-01-08 18:22:07 -080061 if (!IsGsiInstalled()) {
David Andersonee84d742019-01-07 18:10:29 -080062 *error = "not detected";
63 return false;
64 }
David Andersonb3aff182019-01-11 14:37:51 -080065
66 std::string boot_key;
67 if (!GetInstallStatus(&boot_key)) {
68 *error = "error ("s + strerror(errno) + ")";
69 return false;
70 }
71
72 // Give up if we've failed to boot kMaxBootAttempts times.
73 int attempts;
74 if (GetBootAttempts(boot_key, &attempts)) {
David Anderson782b3232019-01-17 14:18:20 -080075 if (attempts + 1 > kMaxBootAttempts) {
David Andersonb3aff182019-01-11 14:37:51 -080076 *error = "exceeded max boot attempts";
77 return false;
78 }
David Anderson782b3232019-01-17 14:18:20 -080079
80 std::string new_key;
81 if (kOnlyAllowSingleBoot) {
82 // Mark the GSI as disabled. This only affects the next boot, not
83 // the current boot.
84 new_key = kInstallStatusDisabled;
85 } else {
86 new_key = std::to_string(attempts + 1);
87 }
David Anderson15d8b4c2019-02-01 16:30:31 -080088 if (!WriteAndSyncFile(new_key, kGsiInstallStatusFile)) {
David Andersonb3aff182019-01-11 14:37:51 -080089 *error = "error ("s + strerror(errno) + ")";
90 return false;
91 }
92 return true;
93 }
94
95 return boot_key == kInstallStatusOk;
David Andersonee84d742019-01-07 18:10:29 -080096}
97
98bool CanBootIntoGsi(std::string* metadata_file, std::string* error) {
David Andersonc053b3b2019-01-08 18:22:07 -080099 // Always delete this as a safety precaution, so we can return to the
100 // original system image. If we're confident GSI will boot, this will
101 // get re-created by MarkSystemAsGsi.
102 android::base::RemoveFileIfExists(kGsiBootedIndicatorFile);
103
David Andersonee84d742019-01-07 18:10:29 -0800104 if (!CanBootIntoGsi(error)) {
David Andersonee84d742019-01-07 18:10:29 -0800105 return false;
106 }
107
David Andersona141ba82019-01-14 19:09:27 -0800108 *metadata_file = kGsiLpMetadataFile;
David Andersonee84d742019-01-07 18:10:29 -0800109 return true;
110}
111
112bool UninstallGsi() {
David Andersona141ba82019-01-14 19:09:27 -0800113 return android::base::WriteStringToFile(kInstallStatusWipe, kGsiInstallStatusFile);
114}
115
116bool DisableGsi() {
117 return android::base::WriteStringToFile(kInstallStatusDisabled, kGsiInstallStatusFile);
David Andersonee84d742019-01-07 18:10:29 -0800118}
119
120bool MarkSystemAsGsi() {
121 return android::base::WriteStringToFile("1", kGsiBootedIndicatorFile);
122}
123
David Andersonb3aff182019-01-11 14:37:51 -0800124bool GetInstallStatus(std::string* status) {
125 return android::base::ReadFileToString(kGsiInstallStatusFile, status);
126}
127
128bool GetBootAttempts(const std::string& boot_key, int* attempts) {
129 return android::base::ParseInt(boot_key, attempts);
130}
131
David Andersonee84d742019-01-07 18:10:29 -0800132} // namespace gsi
133} // namespace android