1 /*
2  * Copyright (C) 2016 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 <bootloader_message/bootloader_message.h>
18 
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <string.h>
22 
23 #include <optional>
24 #include <string>
25 #include <string_view>
26 #include <vector>
27 
28 #include <android-base/file.h>
29 #include <android-base/properties.h>
30 #include <android-base/stringprintf.h>
31 #include <android-base/unique_fd.h>
32 #include <fstab/fstab.h>
33 
34 #ifndef __ANDROID__
35 #include <cutils/memory.h>  // for strlcpy
36 #endif
37 
38 using android::fs_mgr::Fstab;
39 using android::fs_mgr::ReadDefaultFstab;
40 
41 static std::optional<std::string> g_misc_device_for_test;
42 
43 // Exposed for test purpose.
SetMiscBlockDeviceForTest(std::string_view misc_device)44 void SetMiscBlockDeviceForTest(std::string_view misc_device) {
45   g_misc_device_for_test = misc_device;
46 }
47 
get_misc_blk_device(std::string * err)48 std::string get_misc_blk_device(std::string* err) {
49   if (g_misc_device_for_test.has_value() && !g_misc_device_for_test->empty()) {
50     return *g_misc_device_for_test;
51   }
52   Fstab fstab;
53   if (!ReadDefaultFstab(&fstab)) {
54     *err = "failed to read default fstab";
55     return "";
56   }
57   for (const auto& entry : fstab) {
58     if (entry.mount_point == "/misc") {
59       return entry.blk_device;
60     }
61   }
62 
63   *err = "failed to find /misc partition";
64   return "";
65 }
66 
67 // In recovery mode, recovery can get started and try to access the misc
68 // device before the kernel has actually created it.
wait_for_device(const std::string & blk_device,std::string * err)69 static bool wait_for_device(const std::string& blk_device, std::string* err) {
70   int tries = 0;
71   int ret;
72   err->clear();
73   do {
74     ++tries;
75     struct stat buf;
76     ret = stat(blk_device.c_str(), &buf);
77     if (ret == -1) {
78       *err += android::base::StringPrintf("failed to stat %s try %d: %s\n",
79                                           blk_device.c_str(), tries, strerror(errno));
80       sleep(1);
81     }
82   } while (ret && tries < 10);
83 
84   if (ret) {
85     *err += android::base::StringPrintf("failed to stat %s\n", blk_device.c_str());
86   }
87   return ret == 0;
88 }
89 
read_misc_partition(void * p,size_t size,const std::string & misc_blk_device,size_t offset,std::string * err)90 static bool read_misc_partition(void* p, size_t size, const std::string& misc_blk_device,
91                                 size_t offset, std::string* err) {
92   if (!wait_for_device(misc_blk_device, err)) {
93     return false;
94   }
95   android::base::unique_fd fd(open(misc_blk_device.c_str(), O_RDONLY));
96   if (fd == -1) {
97     *err = android::base::StringPrintf("failed to open %s: %s", misc_blk_device.c_str(),
98                                        strerror(errno));
99     return false;
100   }
101   if (lseek(fd, static_cast<off_t>(offset), SEEK_SET) != static_cast<off_t>(offset)) {
102     *err = android::base::StringPrintf("failed to lseek %s: %s", misc_blk_device.c_str(),
103                                        strerror(errno));
104     return false;
105   }
106   if (!android::base::ReadFully(fd, p, size)) {
107     *err = android::base::StringPrintf("failed to read %s: %s", misc_blk_device.c_str(),
108                                        strerror(errno));
109     return false;
110   }
111   return true;
112 }
113 
write_misc_partition(const void * p,size_t size,const std::string & misc_blk_device,size_t offset,std::string * err)114 bool write_misc_partition(const void* p, size_t size, const std::string& misc_blk_device,
115                           size_t offset, std::string* err) {
116   android::base::unique_fd fd(open(misc_blk_device.c_str(), O_WRONLY));
117   if (fd == -1) {
118     *err = android::base::StringPrintf("failed to open %s: %s", misc_blk_device.c_str(),
119                                        strerror(errno));
120     return false;
121   }
122   if (lseek(fd, static_cast<off_t>(offset), SEEK_SET) != static_cast<off_t>(offset)) {
123     *err = android::base::StringPrintf("failed to lseek %s: %s", misc_blk_device.c_str(),
124                                        strerror(errno));
125     return false;
126   }
127   if (!android::base::WriteFully(fd, p, size)) {
128     *err = android::base::StringPrintf("failed to write %s: %s", misc_blk_device.c_str(),
129                                        strerror(errno));
130     return false;
131   }
132   if (fsync(fd) == -1) {
133     *err = android::base::StringPrintf("failed to fsync %s: %s", misc_blk_device.c_str(),
134                                        strerror(errno));
135     return false;
136   }
137   return true;
138 }
139 
get_bootloader_message_blk_device(std::string * err)140 std::string get_bootloader_message_blk_device(std::string* err) {
141   std::string misc_blk_device = get_misc_blk_device(err);
142   if (misc_blk_device.empty()) return "";
143   if (!wait_for_device(misc_blk_device, err)) return "";
144   return misc_blk_device;
145 }
146 
read_bootloader_message_from(bootloader_message * boot,const std::string & misc_blk_device,std::string * err)147 bool read_bootloader_message_from(bootloader_message* boot, const std::string& misc_blk_device,
148                                   std::string* err) {
149   return read_misc_partition(boot, sizeof(*boot), misc_blk_device,
150                              BOOTLOADER_MESSAGE_OFFSET_IN_MISC, err);
151 }
152 
read_bootloader_message(bootloader_message * boot,std::string * err)153 bool read_bootloader_message(bootloader_message* boot, std::string* err) {
154   std::string misc_blk_device = get_misc_blk_device(err);
155   if (misc_blk_device.empty()) {
156     return false;
157   }
158   return read_bootloader_message_from(boot, misc_blk_device, err);
159 }
160 
write_bootloader_message_to(const bootloader_message & boot,const std::string & misc_blk_device,std::string * err)161 bool write_bootloader_message_to(const bootloader_message& boot, const std::string& misc_blk_device,
162                                  std::string* err) {
163   return write_misc_partition(&boot, sizeof(boot), misc_blk_device,
164                               BOOTLOADER_MESSAGE_OFFSET_IN_MISC, err);
165 }
166 
write_bootloader_message(const bootloader_message & boot,std::string * err)167 bool write_bootloader_message(const bootloader_message& boot, std::string* err) {
168   std::string misc_blk_device = get_misc_blk_device(err);
169   if (misc_blk_device.empty()) {
170     return false;
171   }
172   return write_bootloader_message_to(boot, misc_blk_device, err);
173 }
174 
clear_bootloader_message(std::string * err)175 bool clear_bootloader_message(std::string* err) {
176   bootloader_message boot = {};
177   return write_bootloader_message(boot, err);
178 }
179 
write_bootloader_message(const std::vector<std::string> & options,std::string * err)180 bool write_bootloader_message(const std::vector<std::string>& options, std::string* err) {
181   bootloader_message boot = {};
182   update_bootloader_message_in_struct(&boot, options);
183 
184   return write_bootloader_message(boot, err);
185 }
186 
write_bootloader_message_to(const std::vector<std::string> & options,const std::string & misc_blk_device,std::string * err)187 bool write_bootloader_message_to(const std::vector<std::string>& options,
188                                  const std::string& misc_blk_device, std::string* err) {
189   bootloader_message boot = {};
190   update_bootloader_message_in_struct(&boot, options);
191 
192   return write_bootloader_message_to(boot, misc_blk_device, err);
193 }
194 
update_bootloader_message(const std::vector<std::string> & options,std::string * err)195 bool update_bootloader_message(const std::vector<std::string>& options, std::string* err) {
196   bootloader_message boot;
197   if (!read_bootloader_message(&boot, err)) {
198     return false;
199   }
200   update_bootloader_message_in_struct(&boot, options);
201 
202   return write_bootloader_message(boot, err);
203 }
204 
update_bootloader_message_in_struct(bootloader_message * boot,const std::vector<std::string> & options)205 bool update_bootloader_message_in_struct(bootloader_message* boot,
206                                          const std::vector<std::string>& options) {
207   if (!boot) return false;
208   // Replace the command & recovery fields.
209   memset(boot->command, 0, sizeof(boot->command));
210   memset(boot->recovery, 0, sizeof(boot->recovery));
211 
212   strlcpy(boot->command, "boot-recovery", sizeof(boot->command));
213 
214   std::string recovery = "recovery\n";
215   for (const auto& s : options) {
216     recovery += s;
217     if (s.back() != '\n') {
218       recovery += '\n';
219     }
220   }
221   strlcpy(boot->recovery, recovery.c_str(), sizeof(boot->recovery));
222   return true;
223 }
224 
write_reboot_bootloader(std::string * err)225 bool write_reboot_bootloader(std::string* err) {
226   bootloader_message boot;
227   if (!read_bootloader_message(&boot, err)) {
228     return false;
229   }
230   if (boot.command[0] != '\0') {
231     *err = "Bootloader command pending.";
232     return false;
233   }
234   strlcpy(boot.command, "bootonce-bootloader", sizeof(boot.command));
235   return write_bootloader_message(boot, err);
236 }
237 
read_wipe_package(std::string * package_data,size_t size,std::string * err)238 bool read_wipe_package(std::string* package_data, size_t size, std::string* err) {
239   std::string misc_blk_device = get_misc_blk_device(err);
240   if (misc_blk_device.empty()) {
241     return false;
242   }
243   package_data->resize(size);
244   return read_misc_partition(&(*package_data)[0], size, misc_blk_device,
245                              WIPE_PACKAGE_OFFSET_IN_MISC, err);
246 }
247 
write_wipe_package(const std::string & package_data,std::string * err)248 bool write_wipe_package(const std::string& package_data, std::string* err) {
249   std::string misc_blk_device = get_misc_blk_device(err);
250   if (misc_blk_device.empty()) {
251     return false;
252   }
253   static constexpr size_t kMaximumWipePackageSize =
254       SYSTEM_SPACE_OFFSET_IN_MISC - WIPE_PACKAGE_OFFSET_IN_MISC;
255   if (package_data.size() > kMaximumWipePackageSize) {
256     *err = "Wipe package size " + std::to_string(package_data.size()) + " exceeds " +
257            std::to_string(kMaximumWipePackageSize) + " bytes";
258     return false;
259   }
260   return write_misc_partition(package_data.data(), package_data.size(), misc_blk_device,
261                               WIPE_PACKAGE_OFFSET_IN_MISC, err);
262 }
263 
ValidateSystemSpaceRegion(size_t offset,size_t size,std::string * err)264 static bool ValidateSystemSpaceRegion(size_t offset, size_t size, std::string* err) {
265   if (size <= SYSTEM_SPACE_SIZE_IN_MISC && offset <= (SYSTEM_SPACE_SIZE_IN_MISC - size)) {
266     return true;
267   }
268   *err = android::base::StringPrintf("Out of bound access (offset %zu size %zu)", offset, size);
269   return false;
270 }
271 
ReadMiscPartitionSystemSpace(void * data,size_t size,size_t offset,std::string * err)272 static bool ReadMiscPartitionSystemSpace(void* data, size_t size, size_t offset, std::string* err) {
273   if (!ValidateSystemSpaceRegion(offset, size, err)) {
274     return false;
275   }
276   auto misc_blk_device = get_misc_blk_device(err);
277   if (misc_blk_device.empty()) {
278     return false;
279   }
280   return read_misc_partition(data, size, misc_blk_device, SYSTEM_SPACE_OFFSET_IN_MISC + offset,
281                              err);
282 }
283 
WriteMiscPartitionSystemSpace(const void * data,size_t size,size_t offset,std::string * err)284 static bool WriteMiscPartitionSystemSpace(const void* data, size_t size, size_t offset,
285                                           std::string* err) {
286   if (!ValidateSystemSpaceRegion(offset, size, err)) {
287     return false;
288   }
289   auto misc_blk_device = get_misc_blk_device(err);
290   if (misc_blk_device.empty()) {
291     return false;
292   }
293   return write_misc_partition(data, size, misc_blk_device, SYSTEM_SPACE_OFFSET_IN_MISC + offset,
294                               err);
295 }
296 
ReadMiscVirtualAbMessage(misc_virtual_ab_message * message,std::string * err)297 bool ReadMiscVirtualAbMessage(misc_virtual_ab_message* message, std::string* err) {
298   return ReadMiscPartitionSystemSpace(message, sizeof(*message),
299                                       offsetof(misc_system_space_layout, virtual_ab_message), err);
300 }
301 
WriteMiscVirtualAbMessage(const misc_virtual_ab_message & message,std::string * err)302 bool WriteMiscVirtualAbMessage(const misc_virtual_ab_message& message, std::string* err) {
303   return WriteMiscPartitionSystemSpace(&message, sizeof(message),
304                                        offsetof(misc_system_space_layout, virtual_ab_message), err);
305 }
306 
write_reboot_bootloader(void)307 extern "C" bool write_reboot_bootloader(void) {
308   std::string err;
309   return write_reboot_bootloader(&err);
310 }
311 
write_bootloader_message(const char * options)312 extern "C" bool write_bootloader_message(const char* options) {
313   std::string err;
314   return write_bootloader_message({options}, &err);
315 }
316