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