/* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "recovery_utils/roots.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "otautil/sysutil.h" // HUANGLONG begin #include #include #include #include "otautil/copyfile.h" // HUANGLONG end using android::fs_mgr::Fstab; using android::fs_mgr::FstabEntry; using android::fs_mgr::ReadDefaultFstab; static Fstab fstab; // HUANGLONG begin #define DEV_DIR "/dev/block/" #define DEV_MOUNTPOINT "/sdcard/" #define SDCARD_MOUNTPOINT "/sdcard" #define MAX_DEV_NAME_LENGTH 64 #define MAX_RETRY_TIMES 10 static const char* FS_TYPES[] = { "vfat", "ntfs", "ext4", "tntfs", nullptr, }; enum { //NAND_TYPE, EMMC_TYPE, NULL_TYPE }; // Check the flash type. int check_flash_type() { int ret = NULL_TYPE; char buffer[1024]; FILE *fp; fp = fopen("/proc/cmdline","r"); if (fp != NULL) { if (fgets(buffer,1024,fp) != NULL) { if (strstr(buffer,"mmcblk")) { ret = EMMC_TYPE; } else { LOG(ERROR) << "check_flash_type, can't get Flash Type in Cmdline " << buffer; } } fclose(fp); } return ret; } // Clearing Partitions int write_emmc_clean(const char* partition) { Volume* vblock = volume_for_mount_point(partition); if (vblock == NULL) { printf("can't find mount point: %s", partition); return -1; } LOG(INFO)<< "write_clean" << vblock->blk_device.c_str() << "begin\n"; FILE* fw = fopen(vblock->blk_device.c_str(), "wb"); if (fw == NULL) { printf("can't fopen %s: %s", partition,strerror(errno)); return -1; } int fw_fd = fileno(fw); unsigned char readbuf[32*1024]; ssize_t n = 0; while (n != -1) { memset(readbuf, 0xff, sizeof(readbuf)); n = write(fw_fd, readbuf, sizeof(readbuf)); } fclose(fw); return 0; } // Clearing Partitions in EMMC_TYPE int write_clean(const char* partition) { LOG(INFO)<< "write_clean" << partition << "begin\n"; int ret = -1 ; if (check_flash_type() == EMMC_TYPE) { ret = write_emmc_clean(partition); } return ret; } // Write a specified partition, eg: /baseparam int write_partition(const char* partitionMountPath, const char* fileToWrite) { Volume* vblock = volume_for_mount_point(partitionMountPath); if (vblock == NULL) { printf("can't find mount point: %s", partitionMountPath); return -1; } return copyFile(fileToWrite, vblock->blk_device.c_str()); } // Wait for the device. The query is performed once every second for 10 times. static void wait_for_device(const char* fn) { int tries = 0; int ret = 0; struct stat buf; do { ++tries; ret = stat(fn, &buf); if (ret) { LOG(ERROR) <<"stat "<d_name), "sd")) { strcpy(dir_buf, DEV_DIR); strcat(dir_buf, dirent->d_name); ret = update_volume(dir_buf); if (ret == 0) { break; } else { continue; } } } closedir(dir); return ret; } // HUANGLONG end constexpr const char* CACHE_ROOT = "/cache"; void load_volume_table() { if (!ReadDefaultFstab(&fstab)) { LOG(ERROR) << "Failed to read default fstab"; return; } fstab.emplace_back(FstabEntry{ .blk_device = "ramdisk", .mount_point = "/tmp", .fs_type = "ramdisk", .length = 0, }); std::cout << "recovery filesystem table" << std::endl << "=========================" << std::endl; for (size_t i = 0; i < fstab.size(); ++i) { const auto& entry = fstab[i]; std::cout << " " << i << " " << entry.mount_point << " " << " " << entry.fs_type << " " << entry.blk_device << " " << entry.length << std::endl; } std::cout << std::endl; } Volume* volume_for_mount_point(const std::string& mount_point) { return android::fs_mgr::GetEntryForMountPoint(&fstab, mount_point); } // Mount the volume specified by path at the given mount_point. int ensure_path_mounted_at(const std::string& path, const std::string& mount_point) { return android::fs_mgr::EnsurePathMounted(&fstab, path, mount_point) ? 0 : -1; } int ensure_path_mounted(const std::string& path) { // Mount at the default mount point. return android::fs_mgr::EnsurePathMounted(&fstab, path) ? 0 : -1; } int ensure_path_unmounted(const std::string& path) { return android::fs_mgr::EnsurePathUnmounted(&fstab, path) ? 0 : -1; } static int exec_cmd(const std::vector& args) { CHECK(!args.empty()); auto argv = StringVectorToNullTerminatedArray(args); pid_t child; if ((child = fork()) == 0) { execv(argv[0], argv.data()); _exit(EXIT_FAILURE); } int status; waitpid(child, &status, 0); if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { LOG(ERROR) << args[0] << " failed with status " << WEXITSTATUS(status); } return WEXITSTATUS(status); } static int64_t get_file_size(int fd, uint64_t reserve_len) { struct stat buf; int ret = fstat(fd, &buf); if (ret) return 0; int64_t computed_size; if (S_ISREG(buf.st_mode)) { computed_size = buf.st_size - reserve_len; } else if (S_ISBLK(buf.st_mode)) { uint64_t block_device_size = get_block_device_size(fd); if (block_device_size < reserve_len || block_device_size > std::numeric_limits::max()) { computed_size = 0; } else { computed_size = block_device_size - reserve_len; } } else { computed_size = 0; } return computed_size; } int format_volume(const std::string& volume, const std::string& directory) { const FstabEntry* v = android::fs_mgr::GetEntryForPath(&fstab, volume); if (v == nullptr) { LOG(ERROR) << "unknown volume \"" << volume << "\""; return -1; } if (v->fs_type == "ramdisk") { LOG(ERROR) << "can't format_volume \"" << volume << "\""; return -1; } if (v->mount_point != volume) { LOG(ERROR) << "can't give path \"" << volume << "\" to format_volume"; return -1; } if (ensure_path_unmounted(volume) != 0) { LOG(ERROR) << "format_volume: Failed to unmount \"" << v->mount_point << "\""; return -1; } if (v->fs_type != "ext4" && v->fs_type != "f2fs") { LOG(ERROR) << "format_volume: fs_type \"" << v->fs_type << "\" unsupported"; return -1; } bool needs_casefold = false; bool needs_projid = false; if (volume == "/data") { needs_casefold = android::base::GetBoolProperty("external_storage.casefold.enabled", false); needs_projid = android::base::GetBoolProperty("external_storage.projid.enabled", false); } // If there's a key_loc that looks like a path, it should be a block device for storing encryption // metadata. Wipe it too. if (!v->key_loc.empty() && v->key_loc[0] == '/') { LOG(INFO) << "Wiping " << v->key_loc; int fd = open(v->key_loc.c_str(), O_WRONLY | O_CREAT, 0644); if (fd == -1) { PLOG(ERROR) << "format_volume: Failed to open " << v->key_loc; return -1; } wipe_block_device(fd, get_file_size(fd)); close(fd); } int64_t length = 0; if (v->length > 0) { length = v->length; } else if (v->length < 0 || v->key_loc == "footer") { android::base::unique_fd fd(open(v->blk_device.c_str(), O_RDONLY)); if (fd == -1) { PLOG(ERROR) << "format_volume: failed to open " << v->blk_device; return -1; } length = get_file_size(fd.get(), v->length ? -v->length : CRYPT_FOOTER_OFFSET); if (length <= 0) { LOG(ERROR) << "get_file_size: invalid size " << length << " for " << v->blk_device; return -1; } } if (v->fs_type == "ext4") { static constexpr int kBlockSize = 4096; std::vector mke2fs_args = { "/system/bin/mke2fs", "-F", "-t", "ext4", "-b", std::to_string(kBlockSize), }; // Project ID's require wider inodes. The Quotas themselves are enabled by tune2fs on boot. if (needs_projid) { mke2fs_args.push_back("-I"); mke2fs_args.push_back("512"); } if (v->fs_mgr_flags.ext_meta_csum) { mke2fs_args.push_back("-O"); mke2fs_args.push_back("metadata_csum"); mke2fs_args.push_back("-O"); mke2fs_args.push_back("64bit"); mke2fs_args.push_back("-O"); mke2fs_args.push_back("extent"); } int raid_stride = v->logical_blk_size / kBlockSize; int raid_stripe_width = v->erase_blk_size / kBlockSize; // stride should be the max of 8KB and logical block size if (v->logical_blk_size != 0 && v->logical_blk_size < 8192) { raid_stride = 8192 / kBlockSize; } if (v->erase_blk_size != 0 && v->logical_blk_size != 0) { mke2fs_args.push_back("-E"); mke2fs_args.push_back( android::base::StringPrintf("stride=%d,stripe-width=%d", raid_stride, raid_stripe_width)); } mke2fs_args.push_back(v->blk_device); if (length != 0) { mke2fs_args.push_back(std::to_string(length / kBlockSize)); } int result = exec_cmd(mke2fs_args); if (result == 0 && !directory.empty()) { std::vector e2fsdroid_args = { "/system/bin/e2fsdroid", "-e", "-f", directory, "-a", volume, v->blk_device, }; result = exec_cmd(e2fsdroid_args); } if (result != 0) { PLOG(ERROR) << "format_volume: Failed to make ext4 on " << v->blk_device; return -1; } return 0; } // Has to be f2fs because we checked earlier. static constexpr int kSectorSize = 4096; std::vector make_f2fs_cmd = { "/system/bin/make_f2fs", "-g", "android", }; if (needs_projid) { make_f2fs_cmd.push_back("-O"); make_f2fs_cmd.push_back("project_quota,extra_attr"); } if (needs_casefold) { make_f2fs_cmd.push_back("-O"); make_f2fs_cmd.push_back("casefold"); make_f2fs_cmd.push_back("-C"); make_f2fs_cmd.push_back("utf8"); } if (v->fs_mgr_flags.fs_compress) { make_f2fs_cmd.push_back("-O"); make_f2fs_cmd.push_back("compression"); make_f2fs_cmd.push_back("-O"); make_f2fs_cmd.push_back("extra_attr"); } make_f2fs_cmd.push_back(v->blk_device); if (length >= kSectorSize) { make_f2fs_cmd.push_back(std::to_string(length / kSectorSize)); } if (exec_cmd(make_f2fs_cmd) != 0) { PLOG(ERROR) << "format_volume: Failed to make_f2fs on " << v->blk_device; return -1; } if (!directory.empty()) { std::vector sload_f2fs_cmd = { "/system/bin/sload_f2fs", "-f", directory, "-t", volume, v->blk_device, }; if (exec_cmd(sload_f2fs_cmd) != 0) { PLOG(ERROR) << "format_volume: Failed to sload_f2fs on " << v->blk_device; return -1; } } return 0; } int format_volume(const std::string& volume) { return format_volume(volume, ""); } int setup_install_mounts() { if (fstab.empty()) { LOG(ERROR) << "can't set up install mounts: no fstab loaded"; return -1; } for (const FstabEntry& entry : fstab) { // We don't want to do anything with "/". if (entry.mount_point == "/") { continue; } // HUANGLONG begin // recovery, Add the /sdcard and /data mount points. if (entry.mount_point == "/tmp" || entry.mount_point == "/cache" || entry.mount_point == "/sdcard" || entry.mount_point == "/data") { // HUANGLONG end if (ensure_path_mounted(entry.mount_point) != 0) { LOG(ERROR) << "Failed to mount " << entry.mount_point; // HUANGLONG begin // return -1; // HUANGLONG end } } else { if (ensure_path_unmounted(entry.mount_point) != 0) { LOG(ERROR) << "Failed to unmount " << entry.mount_point; return -1; } } } return 0; } bool HasCache() { CHECK(!fstab.empty()); static bool has_cache = volume_for_mount_point(CACHE_ROOT) != nullptr; return has_cache; }