diff options
Diffstat (limited to 'otautil')
-rw-r--r-- | otautil/Android.bp | 11 | ||||
-rw-r--r-- | otautil/include/otautil/roots.h | 59 | ||||
-rw-r--r-- | otautil/include/otautil/sysutil.h | 51 | ||||
-rw-r--r-- | otautil/roots.cpp | 285 | ||||
-rw-r--r-- | otautil/sysutil.cpp | 151 |
5 files changed, 486 insertions, 71 deletions
diff --git a/otautil/Android.bp b/otautil/Android.bp index 41018dd2f..b4936c08b 100644 --- a/otautil/Android.bp +++ b/otautil/Android.bp @@ -42,12 +42,23 @@ cc_library_static { "dirutil.cpp", "mounts.cpp", "parse_install_logs.cpp", + "roots.cpp", "sysutil.cpp", "thermalutil.cpp", ], + include_dirs: [ + "system/vold", + ], + + static_libs: [ + "libfstab", + ], + shared_libs: [ "libcutils", + "libext4_utils", + "libfs_mgr", "libselinux", ], }, diff --git a/otautil/include/otautil/roots.h b/otautil/include/otautil/roots.h new file mode 100644 index 000000000..482f3d050 --- /dev/null +++ b/otautil/include/otautil/roots.h @@ -0,0 +1,59 @@ +/* + * 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. + */ + +#pragma once + +#include <string> + +#include <fstab/fstab.h> + +using Volume = android::fs_mgr::FstabEntry; + +// Load and parse volume data from /etc/recovery.fstab. +void load_volume_table(); + +// Return the Volume* record for this mount point (or nullptr). +Volume* volume_for_mount_point(const std::string& mount_point); + +// Make sure that the volume 'path' is on is mounted. Returns 0 on +// success (volume is mounted). +int ensure_path_mounted(const std::string& path); + +// Similar to ensure_path_mounted, but allows one to specify the mount_point. +int ensure_path_mounted_at(const std::string& path, const std::string& mount_point); + +// Make sure that the volume 'path' is on is unmounted. Returns 0 on +// success (volume is unmounted); +int ensure_path_unmounted(const std::string& path); + +// Reformat the given volume (must be the mount point only, eg +// "/cache"), no paths permitted. Attempts to unmount the volume if +// it is mounted. +int format_volume(const std::string& volume); + +// Reformat the given volume (must be the mount point only, eg +// "/cache"), no paths permitted. Attempts to unmount the volume if +// it is mounted. +// Copies 'directory' to root of the newly formatted volume +int format_volume(const std::string& volume, const std::string& directory); + +// Ensure that all and only the volumes that packages expect to find +// mounted (/tmp and /cache) are mounted. Returns 0 on success. +int setup_install_mounts(); + +bool logical_partitions_mapped(); + +std::string get_system_root(); diff --git a/otautil/include/otautil/sysutil.h b/otautil/include/otautil/sysutil.h index 2eeb7c302..692a99e9d 100644 --- a/otautil/include/otautil/sysutil.h +++ b/otautil/include/otautil/sysutil.h @@ -22,6 +22,57 @@ #include <string> #include <vector> +#include "rangeset.h" + +// This class holds the content of a block map file. +class BlockMapData { + public: + // A "block map" which looks like this (from uncrypt/uncrypt.cpp): + // + // /dev/block/platform/msm_sdcc.1/by-name/userdata # block device + // 49652 4096 # file size in bytes, block size + // 3 # count of block ranges + // 1000 1008 # block range 0 + // 2100 2102 # ... block range 1 + // 30 33 # ... block range 2 + // + // Each block range represents a half-open interval; the line "30 33" reprents the blocks + // [30, 31, 32]. + static BlockMapData ParseBlockMapFile(const std::string& block_map_path); + + explicit operator bool() const { + return !path_.empty(); + } + + std::string path() const { + return path_; + } + uint64_t file_size() const { + return file_size_; + } + uint32_t block_size() const { + return block_size_; + } + RangeSet block_ranges() const { + return block_ranges_; + } + + private: + BlockMapData() = default; + + BlockMapData(const std::string& path, uint64_t file_size, uint32_t block_size, + RangeSet block_ranges) + : path_(path), + file_size_(file_size), + block_size_(block_size), + block_ranges_(std::move(block_ranges)) {} + + std::string path_; + uint64_t file_size_ = 0; + uint32_t block_size_ = 0; + RangeSet block_ranges_; +}; + /* * Use this to keep track of mapped segments. */ diff --git a/otautil/roots.cpp b/otautil/roots.cpp new file mode 100644 index 000000000..815d644e5 --- /dev/null +++ b/otautil/roots.cpp @@ -0,0 +1,285 @@ +/* + * 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 "otautil/roots.h" + +#include <ctype.h> +#include <fcntl.h> +#include <inttypes.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <sys/mount.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> + +#include <iostream> +#include <string> +#include <vector> + +#include <android-base/logging.h> +#include <android-base/properties.h> +#include <android-base/stringprintf.h> +#include <android-base/unique_fd.h> +#include <cryptfs.h> +#include <ext4_utils/wipe.h> +#include <fs_mgr.h> +#include <fs_mgr/roots.h> +#include <fs_mgr_dm_linear.h> + +#include "otautil/mounts.h" +#include "otautil/sysutil.h" + +using android::fs_mgr::Fstab; +using android::fs_mgr::FstabEntry; +using android::fs_mgr::ReadDefaultFstab; + +static Fstab fstab; + +void load_volume_table() { + if (!ReadDefaultFstab(&fstab)) { + LOG(ERROR) << "Failed to read default fstab"; + return; + } + + fstab.emplace_back(FstabEntry{ + .mount_point = "/tmp", .fs_type = "ramdisk", .blk_device = "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<std::string>& 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<int64_t>::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; + } + + // 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<std::string> mke2fs_args = { + "/system/bin/mke2fs", "-F", "-t", "ext4", "-b", std::to_string(kBlockSize), + }; + + 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<std::string> 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<std::string> make_f2fs_cmd = { + "/system/bin/make_f2fs", + "-g", + "android", + 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<std::string> 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; + } + + if (entry.mount_point == "/tmp" || entry.mount_point == "/cache") { + if (ensure_path_mounted(entry.mount_point) != 0) { + LOG(ERROR) << "Failed to mount " << entry.mount_point; + return -1; + } + } else { + if (ensure_path_unmounted(entry.mount_point) != 0) { + LOG(ERROR) << "Failed to unmount " << entry.mount_point; + return -1; + } + } + } + return 0; +} + +bool logical_partitions_mapped() { + return android::fs_mgr::LogicalPartitionsMapped(); +} + +std::string get_system_root() { + return android::fs_mgr::GetSystemRoot(); +} diff --git a/otautil/sysutil.cpp b/otautil/sysutil.cpp index d8969a0bb..8366fa0ac 100644 --- a/otautil/sysutil.cpp +++ b/otautil/sysutil.cpp @@ -18,12 +18,13 @@ #include <errno.h> // TEMP_FAILURE_RETRY #include <fcntl.h> -#include <stdint.h> // SIZE_MAX +#include <inttypes.h> #include <sys/mman.h> #include <sys/stat.h> #include <sys/types.h> #include <algorithm> +#include <limits> #include <string> #include <vector> @@ -34,6 +35,68 @@ #include <android-base/unique_fd.h> #include <cutils/android_reboot.h> +BlockMapData BlockMapData::ParseBlockMapFile(const std::string& block_map_path) { + std::string content; + if (!android::base::ReadFileToString(block_map_path, &content)) { + LOG(ERROR) << "Failed to read " << block_map_path; + return {}; + } + + std::vector<std::string> lines = android::base::Split(android::base::Trim(content), "\n"); + if (lines.size() < 4) { + LOG(ERROR) << "Block map file is too short: " << lines.size(); + return {}; + } + + const std::string& block_dev = lines[0]; + + uint64_t file_size; + uint32_t blksize; + if (sscanf(lines[1].c_str(), "%" SCNu64 "%" SCNu32, &file_size, &blksize) != 2) { + LOG(ERROR) << "Failed to parse file size and block size: " << lines[1]; + return {}; + } + + if (file_size == 0 || blksize == 0) { + LOG(ERROR) << "Invalid size in block map file: size " << file_size << ", blksize " << blksize; + return {}; + } + + size_t range_count; + if (sscanf(lines[2].c_str(), "%zu", &range_count) != 1) { + LOG(ERROR) << "Failed to parse block map header: " << lines[2]; + return {}; + } + + uint64_t blocks = ((file_size - 1) / blksize) + 1; + if (blocks > std::numeric_limits<uint32_t>::max() || range_count == 0 || + lines.size() != 3 + range_count) { + LOG(ERROR) << "Invalid data in block map file: size " << file_size << ", blksize " << blksize + << ", range_count " << range_count << ", lines " << lines.size(); + return {}; + } + + RangeSet ranges; + uint64_t remaining_blocks = blocks; + for (size_t i = 0; i < range_count; ++i) { + const std::string& line = lines[i + 3]; + uint64_t start, end; + if (sscanf(line.c_str(), "%" SCNu64 "%" SCNu64, &start, &end) != 2) { + LOG(ERROR) << "failed to parse range " << i << ": " << line; + return {}; + } + uint64_t range_blocks = end - start; + if (end <= start || range_blocks > remaining_blocks) { + LOG(ERROR) << "Invalid range: " << start << " " << end; + return {}; + } + ranges.PushBack({ start, end }); + remaining_blocks -= range_blocks; + } + + return BlockMapData(block_dev, file_size, blksize, std::move(ranges)); +} + bool MemMapping::MapFD(int fd) { struct stat sb; if (fstat(fd, &sb) == -1) { @@ -55,115 +118,61 @@ bool MemMapping::MapFD(int fd) { return true; } -// A "block map" which looks like this (from uncrypt/uncrypt.cpp): -// -// /dev/block/platform/msm_sdcc.1/by-name/userdata # block device -// 49652 4096 # file size in bytes, block size -// 3 # count of block ranges -// 1000 1008 # block range 0 -// 2100 2102 # ... block range 1 -// 30 33 # ... block range 2 -// -// Each block range represents a half-open interval; the line "30 33" reprents the blocks -// [30, 31, 32]. bool MemMapping::MapBlockFile(const std::string& filename) { - std::string content; - if (!android::base::ReadFileToString(filename, &content)) { - PLOG(ERROR) << "Failed to read " << filename; - return false; - } - - std::vector<std::string> lines = android::base::Split(android::base::Trim(content), "\n"); - if (lines.size() < 4) { - LOG(ERROR) << "Block map file is too short: " << lines.size(); - return false; - } - - size_t size; - size_t blksize; - if (sscanf(lines[1].c_str(), "%zu %zu", &size, &blksize) != 2) { - LOG(ERROR) << "Failed to parse file size and block size: " << lines[1]; - return false; - } - - size_t range_count; - if (sscanf(lines[2].c_str(), "%zu", &range_count) != 1) { - LOG(ERROR) << "Failed to parse block map header: " << lines[2]; + auto block_map_data = BlockMapData::ParseBlockMapFile(filename); + if (!block_map_data) { return false; } - size_t blocks; - if (blksize != 0) { - blocks = ((size - 1) / blksize) + 1; - } - if (size == 0 || blksize == 0 || blocks > SIZE_MAX / blksize || range_count == 0 || - lines.size() != 3 + range_count) { - LOG(ERROR) << "Invalid data in block map file: size " << size << ", blksize " << blksize - << ", range_count " << range_count << ", lines " << lines.size(); + if (block_map_data.file_size() > std::numeric_limits<size_t>::max()) { + LOG(ERROR) << "File size is too large for mmap " << block_map_data.file_size(); return false; } // Reserve enough contiguous address space for the whole file. + uint32_t blksize = block_map_data.block_size(); + uint64_t blocks = ((block_map_data.file_size() - 1) / blksize) + 1; void* reserve = mmap(nullptr, blocks * blksize, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0); if (reserve == MAP_FAILED) { PLOG(ERROR) << "failed to reserve address space"; return false; } - const std::string& block_dev = lines[0]; - android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(block_dev.c_str(), O_RDONLY))); + android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(block_map_data.path().c_str(), O_RDONLY))); if (fd == -1) { - PLOG(ERROR) << "failed to open block device " << block_dev; + PLOG(ERROR) << "failed to open block device " << block_map_data.path(); munmap(reserve, blocks * blksize); return false; } ranges_.clear(); - unsigned char* next = static_cast<unsigned char*>(reserve); + auto next = static_cast<unsigned char*>(reserve); size_t remaining_size = blocks * blksize; - bool success = true; - for (size_t i = 0; i < range_count; ++i) { - const std::string& line = lines[i + 3]; - - size_t start, end; - if (sscanf(line.c_str(), "%zu %zu\n", &start, &end) != 2) { - LOG(ERROR) << "failed to parse range " << i << ": " << line; - success = false; - break; - } + for (const auto& [start, end] : block_map_data.block_ranges()) { size_t range_size = (end - start) * blksize; - if (end <= start || (end - start) > SIZE_MAX / blksize || range_size > remaining_size) { - LOG(ERROR) << "Invalid range: " << start << " " << end; - success = false; - break; - } - void* range_start = mmap(next, range_size, PROT_READ, MAP_PRIVATE | MAP_FIXED, fd, static_cast<off_t>(start) * blksize); if (range_start == MAP_FAILED) { - PLOG(ERROR) << "failed to map range " << i << ": " << line; - success = false; - break; + PLOG(ERROR) << "failed to map range " << start << ": " << end; + munmap(reserve, blocks * blksize); + return false; } ranges_.emplace_back(MappedRange{ range_start, range_size }); next += range_size; remaining_size -= range_size; } - if (success && remaining_size != 0) { + if (remaining_size != 0) { LOG(ERROR) << "Invalid ranges: remaining_size " << remaining_size; - success = false; - } - if (!success) { munmap(reserve, blocks * blksize); return false; } addr = static_cast<unsigned char*>(reserve); - length = size; + length = block_map_data.file_size(); - LOG(INFO) << "mmapped " << range_count << " ranges"; + LOG(INFO) << "mmapped " << block_map_data.block_ranges().size() << " ranges"; return true; } |