1 /* 2 * Copyright (C) 2007 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 "otautil/roots.h" 18 19 #include <ctype.h> 20 #include <fcntl.h> 21 #include <inttypes.h> 22 #include <stdint.h> 23 #include <stdlib.h> 24 #include <string.h> 25 #include <sys/mount.h> 26 #include <sys/stat.h> 27 #include <sys/types.h> 28 #include <sys/wait.h> 29 #include <unistd.h> 30 31 #include <iostream> 32 #include <string> 33 #include <vector> 34 35 #include <android-base/logging.h> 36 #include <android-base/properties.h> 37 #include <android-base/stringprintf.h> 38 #include <android-base/unique_fd.h> 39 #include <cryptfs.h> 40 #include <ext4_utils/wipe.h> 41 #include <fs_mgr.h> 42 #include <fs_mgr/roots.h> 43 #include <fs_mgr_dm_linear.h> 44 45 #include "otautil/mounts.h" 46 #include "otautil/sysutil.h" 47 48 using android::fs_mgr::Fstab; 49 using android::fs_mgr::FstabEntry; 50 using android::fs_mgr::ReadDefaultFstab; 51 52 static Fstab fstab; 53 54 void load_volume_table() { 55 if (!ReadDefaultFstab(&fstab)) { 56 LOG(ERROR) << "Failed to read default fstab"; 57 return; 58 } 59 60 fstab.emplace_back(FstabEntry{ 61 .mount_point = "/tmp", .fs_type = "ramdisk", .blk_device = "ramdisk", .length = 0 }); 62 63 std::cout << "recovery filesystem table" << std::endl << "=========================" << std::endl; 64 for (size_t i = 0; i < fstab.size(); ++i) { 65 const auto& entry = fstab[i]; 66 std::cout << " " << i << " " << entry.mount_point << " " 67 << " " << entry.fs_type << " " << entry.blk_device << " " << entry.length 68 << std::endl; 69 } 70 std::cout << std::endl; 71 } 72 73 Volume* volume_for_mount_point(const std::string& mount_point) { 74 return android::fs_mgr::GetEntryForMountPoint(&fstab, mount_point); 75 } 76 77 // Mount the volume specified by path at the given mount_point. 78 int ensure_path_mounted_at(const std::string& path, const std::string& mount_point) { 79 return android::fs_mgr::EnsurePathMounted(&fstab, path, mount_point) ? 0 : -1; 80 } 81 82 int ensure_path_mounted(const std::string& path) { 83 // Mount at the default mount point. 84 return android::fs_mgr::EnsurePathMounted(&fstab, path) ? 0 : -1; 85 } 86 87 int ensure_path_unmounted(const std::string& path) { 88 return android::fs_mgr::EnsurePathUnmounted(&fstab, path) ? 0 : -1; 89 } 90 91 static int exec_cmd(const std::vector<std::string>& args) { 92 CHECK(!args.empty()); 93 auto argv = StringVectorToNullTerminatedArray(args); 94 95 pid_t child; 96 if ((child = fork()) == 0) { 97 execv(argv[0], argv.data()); 98 _exit(EXIT_FAILURE); 99 } 100 101 int status; 102 waitpid(child, &status, 0); 103 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { 104 LOG(ERROR) << args[0] << " failed with status " << WEXITSTATUS(status); 105 } 106 return WEXITSTATUS(status); 107 } 108 109 static int64_t get_file_size(int fd, uint64_t reserve_len) { 110 struct stat buf; 111 int ret = fstat(fd, &buf); 112 if (ret) return 0; 113 114 int64_t computed_size; 115 if (S_ISREG(buf.st_mode)) { 116 computed_size = buf.st_size - reserve_len; 117 } else if (S_ISBLK(buf.st_mode)) { 118 uint64_t block_device_size = get_block_device_size(fd); 119 if (block_device_size < reserve_len || 120 block_device_size > std::numeric_limits<int64_t>::max()) { 121 computed_size = 0; 122 } else { 123 computed_size = block_device_size - reserve_len; 124 } 125 } else { 126 computed_size = 0; 127 } 128 129 return computed_size; 130 } 131 132 int format_volume(const std::string& volume, const std::string& directory) { 133 const FstabEntry* v = android::fs_mgr::GetEntryForPath(&fstab, volume); 134 if (v == nullptr) { 135 LOG(ERROR) << "unknown volume \"" << volume << "\""; 136 return -1; 137 } 138 if (v->fs_type == "ramdisk") { 139 LOG(ERROR) << "can't format_volume \"" << volume << "\""; 140 return -1; 141 } 142 if (v->mount_point != volume) { 143 LOG(ERROR) << "can't give path \"" << volume << "\" to format_volume"; 144 return -1; 145 } 146 if (ensure_path_unmounted(volume) != 0) { 147 LOG(ERROR) << "format_volume: Failed to unmount \"" << v->mount_point << "\""; 148 return -1; 149 } 150 if (v->fs_type != "ext4" && v->fs_type != "f2fs") { 151 LOG(ERROR) << "format_volume: fs_type \"" << v->fs_type << "\" unsupported"; 152 return -1; 153 } 154 155 // If there's a key_loc that looks like a path, it should be a block device for storing encryption 156 // metadata. Wipe it too. 157 if (!v->key_loc.empty() && v->key_loc[0] == '/') { 158 LOG(INFO) << "Wiping " << v->key_loc; 159 int fd = open(v->key_loc.c_str(), O_WRONLY | O_CREAT, 0644); 160 if (fd == -1) { 161 PLOG(ERROR) << "format_volume: Failed to open " << v->key_loc; 162 return -1; 163 } 164 wipe_block_device(fd, get_file_size(fd)); 165 close(fd); 166 } 167 168 int64_t length = 0; 169 if (v->length > 0) { 170 length = v->length; 171 } else if (v->length < 0 || v->key_loc == "footer") { 172 android::base::unique_fd fd(open(v->blk_device.c_str(), O_RDONLY)); 173 if (fd == -1) { 174 PLOG(ERROR) << "format_volume: failed to open " << v->blk_device; 175 return -1; 176 } 177 length = get_file_size(fd.get(), v->length ? -v->length : CRYPT_FOOTER_OFFSET); 178 if (length <= 0) { 179 LOG(ERROR) << "get_file_size: invalid size " << length << " for " << v->blk_device; 180 return -1; 181 } 182 } 183 184 if (v->fs_type == "ext4") { 185 static constexpr int kBlockSize = 4096; 186 std::vector<std::string> mke2fs_args = { 187 "/system/bin/mke2fs", "-F", "-t", "ext4", "-b", std::to_string(kBlockSize), 188 }; 189 190 int raid_stride = v->logical_blk_size / kBlockSize; 191 int raid_stripe_width = v->erase_blk_size / kBlockSize; 192 // stride should be the max of 8KB and logical block size 193 if (v->logical_blk_size != 0 && v->logical_blk_size < 8192) { 194 raid_stride = 8192 / kBlockSize; 195 } 196 if (v->erase_blk_size != 0 && v->logical_blk_size != 0) { 197 mke2fs_args.push_back("-E"); 198 mke2fs_args.push_back( 199 android::base::StringPrintf("stride=%d,stripe-width=%d", raid_stride, raid_stripe_width)); 200 } 201 mke2fs_args.push_back(v->blk_device); 202 if (length != 0) { 203 mke2fs_args.push_back(std::to_string(length / kBlockSize)); 204 } 205 206 int result = exec_cmd(mke2fs_args); 207 if (result == 0 && !directory.empty()) { 208 std::vector<std::string> e2fsdroid_args = { 209 "/system/bin/e2fsdroid", "-e", "-f", directory, "-a", volume, v->blk_device, 210 }; 211 result = exec_cmd(e2fsdroid_args); 212 } 213 214 if (result != 0) { 215 PLOG(ERROR) << "format_volume: Failed to make ext4 on " << v->blk_device; 216 return -1; 217 } 218 return 0; 219 } 220 221 // Has to be f2fs because we checked earlier. 222 static constexpr int kSectorSize = 4096; 223 std::vector<std::string> make_f2fs_cmd = { 224 "/system/bin/make_f2fs", 225 "-g", 226 "android", 227 v->blk_device, 228 }; 229 if (length >= kSectorSize) { 230 make_f2fs_cmd.push_back(std::to_string(length / kSectorSize)); 231 } 232 233 if (exec_cmd(make_f2fs_cmd) != 0) { 234 PLOG(ERROR) << "format_volume: Failed to make_f2fs on " << v->blk_device; 235 return -1; 236 } 237 if (!directory.empty()) { 238 std::vector<std::string> sload_f2fs_cmd = { 239 "/system/bin/sload_f2fs", "-f", directory, "-t", volume, v->blk_device, 240 }; 241 if (exec_cmd(sload_f2fs_cmd) != 0) { 242 PLOG(ERROR) << "format_volume: Failed to sload_f2fs on " << v->blk_device; 243 return -1; 244 } 245 } 246 return 0; 247 } 248 249 int format_volume(const std::string& volume) { 250 return format_volume(volume, ""); 251 } 252 253 int setup_install_mounts() { 254 if (fstab.empty()) { 255 LOG(ERROR) << "can't set up install mounts: no fstab loaded"; 256 return -1; 257 } 258 for (const FstabEntry& entry : fstab) { 259 // We don't want to do anything with "/". 260 if (entry.mount_point == "/") { 261 continue; 262 } 263 264 if (entry.mount_point == "/tmp" || entry.mount_point == "/cache") { 265 if (ensure_path_mounted(entry.mount_point) != 0) { 266 LOG(ERROR) << "Failed to mount " << entry.mount_point; 267 return -1; 268 } 269 } else { 270 if (ensure_path_unmounted(entry.mount_point) != 0) { 271 LOG(ERROR) << "Failed to unmount " << entry.mount_point; 272 return -1; 273 } 274 } 275 } 276 return 0; 277 } 278 279 bool logical_partitions_mapped() { 280 return android::fs_mgr::LogicalPartitionsMapped(); 281 } 282 283 std::string get_system_root() { 284 return android::fs_mgr::GetSystemRoot(); 285 } 286