diff options
author | David Anderson <dvander@google.com> | 2019-07-01 19:05:35 -0700 |
---|---|---|
committer | David Anderson <dvander@google.com> | 2019-07-19 13:45:19 -0700 |
commit | 64b53fb4406636ee9fab6a310dafb63dcc28fc2b (patch) | |
tree | 2f8d4fba0b41d4c0ade426562467722b344ff7ae | |
parent | 9bdf86390f9bcd530f616967d82e66d3d53a6bfa (diff) | |
download | gsid-64b53fb4406636ee9fab6a310dafb63dcc28fc2b.tar.gz |
Refactor GsiService/GsiInstaller to use ImageManager.
Since ImageManager was mostly lifted from gsi_installer.cpp, it is
straightforward to remove this code and transition entirely to
the new libfiemap.
Bug: 134536978
Test: gsi_tool install, enable/disable, status, wipe
Change-Id: Ic5e19906cfce9018fd5f9029e1e4de3852dbc5a9
-rw-r--r-- | file_paths.h | 1 | ||||
-rw-r--r-- | gsi_installer.cpp | 431 | ||||
-rw-r--r-- | gsi_installer.h | 60 | ||||
-rw-r--r-- | gsi_service.cpp | 45 | ||||
-rw-r--r-- | gsi_service.h | 14 | ||||
-rw-r--r-- | libfiemap/image_manager.cpp | 55 | ||||
-rw-r--r-- | libfiemap/include/libfiemap/image_manager.h | 29 |
7 files changed, 178 insertions, 457 deletions
diff --git a/file_paths.h b/file_paths.h index 678a7a3..8d1ed0a 100644 --- a/file_paths.h +++ b/file_paths.h @@ -22,6 +22,7 @@ namespace gsi { static constexpr char kDefaultDsuImageFolder[] = "/data/gsi/dsu/"; static constexpr char kUserdataDevice[] = "/dev/block/by-name/userdata"; +static constexpr char kDsuMetadataDir[] = "/metadata/gsi/dsu"; static constexpr char kDsuLpMetadataFile[] = "/metadata/gsi/dsu/lp_metadata"; static constexpr char kDsuOneShotBootFile[] = "/metadata/gsi/dsu/one_shot_boot"; static constexpr char kDsuInstallDirFile[] = "/metadata/gsi/dsu/install_dir"; diff --git a/gsi_installer.cpp b/gsi_installer.cpp index 202c345..ea20f4d 100644 --- a/gsi_installer.cpp +++ b/gsi_installer.cpp @@ -28,7 +28,6 @@ #include "file_paths.h" #include "gsi_service.h" -#include "libfiemap/utility.h" #include "libgsi_private.h" namespace android { @@ -43,12 +42,8 @@ using android::base::unique_fd; // The default size of userdata.img for GSI. // We are looking for /data to have atleast 40% free space static constexpr uint32_t kMinimumFreeSpaceThreshold = 40; -// We determine the fragmentation by making sure the files -// we create don't have more than 16 extents. -static constexpr uint32_t kMaximumExtents = 512; // Default userdata image size. static constexpr int64_t kDefaultUserdataSize = int64_t(2) * 1024 * 1024 * 1024; -static constexpr std::chrono::milliseconds kDmTimeout = 5000ms; GsiInstaller::GsiInstaller(GsiService* service, const GsiInstallParams& params) : service_(service), @@ -56,16 +51,17 @@ GsiInstaller::GsiInstaller(GsiService* service, const GsiInstallParams& params) gsi_size_(params.gsiSize), wipe_userdata_(params.wipeUserdata) { userdata_size_ = (params.userdataSize) ? params.userdataSize : kDefaultUserdataSize; - userdata_gsi_path_ = GetImagePath("userdata_gsi"); - system_gsi_path_ = GetImagePath("system_gsi"); + images_ = ImageManager::Open(kDsuMetadataDir, install_dir_); // Only rm userdata_gsi if one didn't already exist. - wipe_userdata_on_failure_ = wipe_userdata_ || access(userdata_gsi_path_.c_str(), F_OK); + if (wipe_userdata_ || !images_->BackingImageExists("userdata_gsi")) { + wipe_userdata_on_failure_ = true; + } } GsiInstaller::GsiInstaller(GsiService* service, const std::string& install_dir) : service_(service), install_dir_(install_dir) { - system_gsi_path_ = GetImagePath("system_gsi"); + images_ = ImageManager::Open(kDsuMetadataDir, install_dir_); // The install already exists, so always mark it as succeeded. succeeded_ = true; @@ -74,21 +70,28 @@ GsiInstaller::GsiInstaller(GsiService* service, const std::string& install_dir) GsiInstaller::~GsiInstaller() { if (!succeeded_) { // Close open handles before we remove files. - system_writer_ = nullptr; - partitions_.clear(); - PostInstallCleanup(); + system_device_ = nullptr; + PostInstallCleanup(images_.get()); GsiService::RemoveGsiFiles(install_dir_, wipe_userdata_on_failure_); } } void GsiInstaller::PostInstallCleanup() { - const auto& dm = DeviceMapper::Instance(); - if (dm.GetState("userdata_gsi") != DmDeviceState::INVALID) { - DestroyLogicalPartition("userdata_gsi"); + auto manager = ImageManager::Open(kDsuMetadataDir, GsiService::GetInstalledImageDir()); + if (!manager) { + LOG(ERROR) << "Could not open image manager"; + return; + } + return PostInstallCleanup(manager.get()); +} + +void GsiInstaller::PostInstallCleanup(ImageManager* manager) { + if (manager->IsImageMapped("userdata_gsi")) { + manager->UnmapImageDevice("userdata_gsi"); } - if (dm.GetState("system_gsi") != DmDeviceState::INVALID) { - DestroyLogicalPartition("system_gsi"); + if (manager->IsImageMapped("system_gsi")) { + manager->UnmapImageDevice("system_gsi"); } } @@ -99,16 +102,13 @@ int GsiInstaller::StartInstall() { if (int status = PreallocateFiles()) { return status; } - if (int status = DetermineReadWriteMethod()) { - return status; - } if (!FormatUserdata()) { return IGsiService::INSTALL_ERROR_GENERIC; } // Map system_gsi so we can write to it. - system_writer_ = OpenPartition("system_gsi"); - if (!system_writer_) { + system_device_ = OpenPartition("system_gsi"); + if (!system_device_) { return IGsiService::INSTALL_ERROR_GENERIC; } @@ -117,32 +117,11 @@ int GsiInstaller::StartInstall() { return IGsiService::INSTALL_OK; } -int GsiInstaller::DetermineReadWriteMethod() { - // If there is a device-mapper node wrapping the block device, then we're - // able to create another node around it; the dm layer does not carry the - // exclusion lock down the stack when a mount occurs. - // - // If there is no intermediate device-mapper node, then partitions cannot be - // opened writable due to sepolicy and exclusivity of having a mounted - // filesystem. This should only happen on devices with no encryption, or - // devices with FBE and no metadata encryption. For these cases it suffices - // to perform normal file writes to /data/gsi (which is unencrypted). - std::string block_device; - if (!FiemapWriter::GetBlockDeviceForFile(system_gsi_path_.c_str(), &block_device, - &can_use_devicemapper_)) { - return IGsiService::INSTALL_ERROR_GENERIC; - } - if (install_dir_ != kDefaultDsuImageFolder && can_use_devicemapper_) { - // Never use device-mapper on external media. We don't support adopted - // storage yet, and accidentally using device-mapper could be dangerous - // as we hardcode the userdata device as backing storage. - LOG(ERROR) << "unexpected device-mapper node used to mount external media"; +int GsiInstaller::PerformSanityChecks() { + if (!images_) { + LOG(ERROR) << "unable to create image manager"; return IGsiService::INSTALL_ERROR_GENERIC; } - return IGsiService::INSTALL_OK; -} - -int GsiInstaller::PerformSanityChecks() { if (gsi_size_ < 0) { LOG(ERROR) << "image size " << gsi_size_ << " is negative"; return IGsiService::INSTALL_ERROR_GENERIC; @@ -179,11 +158,9 @@ int GsiInstaller::PerformSanityChecks() { int GsiInstaller::PreallocateFiles() { if (wipe_userdata_) { - SplitFiemap::RemoveSplitFiles(userdata_gsi_path_); + images_->DeleteBackingImage("userdata_gsi"); } - SplitFiemap::RemoveSplitFiles(system_gsi_path_); - - // TODO: trigger GC from fiemap writer. + images_->DeleteBackingImage("system_gsi"); // Create fallocated files. if (int status = PreallocateUserdata()) { @@ -193,161 +170,44 @@ int GsiInstaller::PreallocateFiles() { return status; } - // Save the extent information in liblp. - metadata_ = CreateMetadata(); - if (!metadata_) { - return IGsiService::INSTALL_ERROR_GENERIC; - } - service_->UpdateProgress(IGsiService::STATUS_COMPLETE, 0); return IGsiService::INSTALL_OK; } int GsiInstaller::PreallocateUserdata() { - int error; - std::unique_ptr<SplitFiemap> userdata_image; - if (wipe_userdata_ || access(userdata_gsi_path_.c_str(), F_OK)) { + if (wipe_userdata_ || !images_->BackingImageExists("userdata_gsi")) { service_->StartAsyncOperation("create userdata", userdata_size_); - userdata_image = CreateFiemapWriter(userdata_gsi_path_, userdata_size_, &error); - if (!userdata_image) { - LOG(ERROR) << "Could not create userdata image: " << userdata_gsi_path_; - return error; + if (!CreateImage("userdata_gsi", userdata_size_, false)) { + LOG(ERROR) << "Could not create userdata image"; + return IGsiService::INSTALL_ERROR_GENERIC; } + // Signal that we need to reformat userdata. wipe_userdata_ = true; - } else { - userdata_image = CreateFiemapWriter(userdata_gsi_path_, 0, &error); - if (!userdata_image) { - LOG(ERROR) << "Could not open userdata image: " << userdata_gsi_path_; - return error; - } - if (userdata_size_ && userdata_image->size() < userdata_size_) { - // :TODO: need to fallocate more blocks and resizefs. - } - userdata_size_ = userdata_image->size(); } - - userdata_block_size_ = userdata_image->block_size(); - - Image image = { - .writer = std::move(userdata_image), - .actual_size = userdata_size_, - }; - partitions_.emplace(std::make_pair("userdata_gsi", std::move(image))); return IGsiService::INSTALL_OK; } int GsiInstaller::PreallocateSystem() { service_->StartAsyncOperation("create system", gsi_size_); - int error; - auto system_image = CreateFiemapWriter(system_gsi_path_, gsi_size_, &error); - if (!system_image) { - return error; + if (!CreateImage("system_gsi", gsi_size_, true)) { + return IGsiService::INSTALL_ERROR_GENERIC; } - - system_block_size_ = system_image->block_size(); - - Image image = { - .writer = std::move(system_image), - .actual_size = gsi_size_, - }; - partitions_.emplace(std::make_pair("system_gsi", std::move(image))); return IGsiService::INSTALL_OK; } -std::unique_ptr<SplitFiemap> GsiInstaller::CreateFiemapWriter(const std::string& path, - uint64_t size, int* error) { - bool create = (size != 0); - - std::function<bool(uint64_t, uint64_t)> progress; - if (create) { - progress = [this](uint64_t bytes, uint64_t /* total */) -> bool { - service_->UpdateProgress(IGsiService::STATUS_WORKING, bytes); - if (service_->should_abort()) return false; - return true; - }; - } - - std::unique_ptr<SplitFiemap> file; - if (!size) { - file = SplitFiemap::Open(path); - } else { - file = SplitFiemap::Create(path, size, 0, std::move(progress)); - } - if (!file) { - LOG(ERROR) << "failed to create or open " << path; - *error = IGsiService::INSTALL_ERROR_GENERIC; - return nullptr; - } - - uint64_t extents = file->extents().size(); - if (extents > kMaximumExtents) { - LOG(ERROR) << "file " << path << " has too many extents: " << extents; - *error = IGsiService::INSTALL_ERROR_FILE_SYSTEM_CLUTTERED; - return nullptr; - } - return file; -} - -// Write data through an fd. -class FdWriter final : public GsiInstaller::WriteHelper { - public: - FdWriter(const std::string& path, unique_fd&& fd) : path_(path), fd_(std::move(fd)) {} - - bool Write(const void* data, uint64_t bytes) override { - return android::base::WriteFully(fd_, data, bytes); - } - bool Flush() override { - if (fsync(fd_)) { - PLOG(ERROR) << "fsync failed: " << path_; - return false; - } +bool GsiInstaller::CreateImage(const std::string& name, uint64_t size, bool readonly) { + auto progress = [this](uint64_t bytes, uint64_t /* total */) -> bool { + service_->UpdateProgress(IGsiService::STATUS_WORKING, bytes); + if (service_->should_abort()) return false; return true; - } - uint64_t Size() override { return get_block_device_size(fd_); } - - private: - std::string path_; - unique_fd fd_; -}; - -// Write data through a SplitFiemap. -class SplitFiemapWriter final : public GsiInstaller::WriteHelper { - public: - explicit SplitFiemapWriter(SplitFiemap* writer) : writer_(writer) {} - - bool Write(const void* data, uint64_t bytes) override { return writer_->Write(data, bytes); } - bool Flush() override { return writer_->Flush(); } - uint64_t Size() override { return writer_->size(); } - - private: - SplitFiemap* writer_; -}; - -std::unique_ptr<GsiInstaller::WriteHelper> GsiInstaller::OpenPartition(const std::string& name) { - if (can_use_devicemapper_) { - std::string path; - if (!CreateLogicalPartition(kUserdataDevice, *metadata_.get(), name, true, kDmTimeout, - &path)) { - LOG(ERROR) << "Error creating device-mapper node for " << name; - return {}; - } - - static const int kOpenFlags = O_RDWR | O_NOFOLLOW | O_CLOEXEC; - unique_fd fd(open(path.c_str(), kOpenFlags)); - if (fd < 0) { - PLOG(ERROR) << "could not open " << path; - } - return std::make_unique<FdWriter>(GetImagePath(name), std::move(fd)); - } + }; + return images_->CreateBackingImage(name, size, readonly, std::move(progress)); +} - auto iter = partitions_.find(name); - if (iter == partitions_.end()) { - LOG(ERROR) << "could not find partition " << name; - return {}; - } - return std::make_unique<SplitFiemapWriter>(iter->second.writer.get()); +std::unique_ptr<MappedDevice> GsiInstaller::OpenPartition(const std::string& name) { + return MappedDevice::Open(images_.get(), 10s, name); } bool GsiInstaller::CommitGsiChunk(int stream_fd, int64_t bytes) { @@ -358,13 +218,13 @@ bool GsiInstaller::CommitGsiChunk(int stream_fd, int64_t bytes) { return false; } - auto buffer = std::make_unique<char[]>(system_block_size_); + static const size_t kBlockSize = 4096; + auto buffer = std::make_unique<char[]>(kBlockSize); int progress = -1; uint64_t remaining = bytes; while (remaining) { - // :TODO: check file pin status! - size_t max_to_read = std::min(system_block_size_, remaining); + size_t max_to_read = std::min(static_cast<uint64_t>(kBlockSize), remaining); ssize_t rv = TEMP_FAILURE_RETRY(read(stream_fd, buffer.get(), max_to_read)); if (rv < 0) { PLOG(ERROR) << "read gsi chunk"; @@ -402,7 +262,7 @@ bool GsiInstaller::CommitGsiChunk(const void* data, size_t bytes) { if (service_->should_abort()) { return false; } - if (!system_writer_->Write(data, bytes)) { + if (!android::base::WriteFully(system_device_->fd(), data, bytes)) { PLOG(ERROR) << "write failed"; return false; } @@ -426,10 +286,6 @@ bool GsiInstaller::SetBootMode(bool one_shot) { return true; } -std::string GsiInstaller::GetImagePath(const std::string& name) { - return GsiService::GetImagePath(install_dir_, name); -} - bool GsiInstaller::CreateInstallStatusFile() { if (!android::base::WriteStringToFile("0", kDsuInstallStatusFile)) { PLOG(ERROR) << "write " << kDsuInstallStatusFile; @@ -438,145 +294,21 @@ bool GsiInstaller::CreateInstallStatusFile() { return true; } -std::unique_ptr<LpMetadata> GsiInstaller::CreateMetadata() { - auto writer = partitions_["system_gsi"].writer.get(); - - std::string data_device_path; - if (install_dir_ == kDefaultDsuImageFolder && !access(kUserdataDevice, F_OK)) { - auto actual_device = GetDevicePathForFile(writer); - if (actual_device != kUserdataDevice) { - LOG(ERROR) << "Image file did not resolve to userdata: " << actual_device; - return nullptr; - } - data_device_path = actual_device; - } else { - data_device_path = writer->bdev_path(); - } - auto data_device_name = android::base::Basename(data_device_path); - - PartitionOpener opener; - BlockDeviceInfo data_device_info; - if (!opener.GetInfo(data_device_path, &data_device_info)) { - LOG(ERROR) << "Error reading userdata partition"; - return nullptr; - } - - std::vector<BlockDeviceInfo> block_devices = {data_device_info}; - auto builder = MetadataBuilder::New(block_devices, data_device_name, 128 * 1024, 1); - if (!builder) { - LOG(ERROR) << "Error creating metadata builder"; - return nullptr; - } - - for (const auto& [name, image] : partitions_) { - uint32_t flags = LP_PARTITION_ATTR_NONE; - if (name == "system_gsi") { - flags |= LP_PARTITION_ATTR_READONLY; - } - Partition* partition = builder->AddPartition(name, flags); - if (!partition) { - LOG(ERROR) << "Error adding " << name << " to partition table"; - return nullptr; - } - if (!AddPartitionFiemap(builder.get(), partition, image, data_device_name)) { - return nullptr; - } - } - - auto metadata = builder->Export(); - if (!metadata) { - LOG(ERROR) << "Error exporting partition table"; - return nullptr; - } - return metadata; -} - -bool GsiInstaller::CreateMetadataFile() { - if (!WriteToImageFile(kDsuLpMetadataFile, *metadata_.get())) { - LOG(ERROR) << "Error writing GSI partition table image"; - return false; - } - return true; -} - bool GsiInstaller::FormatUserdata() { - auto writer = OpenPartition("userdata_gsi"); - if (!writer) { + auto device = OpenPartition("userdata_gsi"); + if (!device) { return false; } // libcutils checks the first 4K, no matter the block size. std::string zeroes(4096, 0); - if (!writer->Write(zeroes.data(), zeroes.size())) { + if (!android::base::WriteFully(device->fd(), zeroes.data(), zeroes.size())) { PLOG(ERROR) << "write userdata_gsi"; return false; } return true; } -bool GsiInstaller::AddPartitionFiemap(MetadataBuilder* builder, Partition* partition, - const Image& image, const std::string& block_device) { - uint64_t sectors_needed = image.actual_size / LP_SECTOR_SIZE; - for (const auto& extent : image.writer->extents()) { - // :TODO: block size check for length, not sector size - if (extent.fe_length % LP_SECTOR_SIZE != 0) { - LOG(ERROR) << "Extent is not sector-aligned: " << extent.fe_length; - return false; - } - if (extent.fe_physical % LP_SECTOR_SIZE != 0) { - LOG(ERROR) << "Extent physical sector is not sector-aligned: " << extent.fe_physical; - return false; - } - - uint64_t num_sectors = - std::min(static_cast<uint64_t>(extent.fe_length / LP_SECTOR_SIZE), sectors_needed); - if (!num_sectors || !sectors_needed) { - // This should never happen, but we include it just in case. It would - // indicate that the last filesystem block had multiple extents. - LOG(WARNING) << "FiemapWriter allocated extra blocks"; - break; - } - - uint64_t physical_sector = extent.fe_physical / LP_SECTOR_SIZE; - if (!builder->AddLinearExtent(partition, block_device, num_sectors, physical_sector)) { - LOG(ERROR) << "Could not add extent to lp metadata"; - return false; - } - - sectors_needed -= num_sectors; - } - return true; -} - -static uint64_t GetPartitionSize(const LpMetadata& metadata, const std::string& name) { - const LpMetadataPartition* partition = FindPartition(metadata, name); - if (!partition) { - return 0; - } - return android::fs_mgr::GetPartitionSize(metadata, *partition); -} - -int GsiInstaller::GetExistingImage(const LpMetadata& metadata, const std::string& name, - Image* image) { - int error; - std::string path = GetImagePath(name); - auto writer = CreateFiemapWriter(path.c_str(), 0, &error); - if (!writer) { - return error; - } - - // Even after recovering the FIEMAP, we also need to know the exact intended - // size of the image, since FiemapWriter may have extended the final block. - uint64_t actual_size = GetPartitionSize(metadata, name); - if (!actual_size) { - LOG(ERROR) << "Could not determine the pre-existing size of " << name; - return IGsiService::INSTALL_ERROR_GENERIC; - } - image->writer = std::move(writer); - image->actual_size = actual_size; - return IGsiService::INSTALL_OK; -} - int GsiInstaller::SetGsiBootable(bool one_shot) { if (gsi_bytes_written_ != gsi_size_) { // We cannot boot if the image is incomplete. @@ -585,16 +317,16 @@ int GsiInstaller::SetGsiBootable(bool one_shot) { return IGsiService::INSTALL_ERROR_GENERIC; } - if (!system_writer_->Flush()) { + if (fsync(system_device_->fd())) { + PLOG(ERROR) << "fsync failed for system_gsi"; return IGsiService::INSTALL_ERROR_GENERIC; } + system_device_ = {}; // If files moved (are no longer pinned), the metadata file will be invalid. - for (const auto& [name, image] : partitions_) { - if (!image.writer->HasPinnedExtents()) { - LOG(ERROR) << name << " no longer has pinned extents"; - return IGsiService::INSTALL_ERROR_GENERIC; - } + // This check can be removed once b/133967059 is fixed. + if (!images_->Validate()) { + return IGsiService::INSTALL_ERROR_GENERIC; } // Remember the installation directory. @@ -605,7 +337,7 @@ int GsiInstaller::SetGsiBootable(bool one_shot) { // Note: create the install status file last, since this is the actual boot // indicator. - if (!CreateMetadataFile() || !SetBootMode(one_shot) || !CreateInstallStatusFile()) { + if (!SetBootMode(one_shot) || !CreateInstallStatusFile()) { return IGsiService::INSTALL_ERROR_GENERIC; } @@ -613,35 +345,12 @@ int GsiInstaller::SetGsiBootable(bool one_shot) { return IGsiService::INSTALL_OK; } -int GsiInstaller::RebuildInstallState() { - if (int error = DetermineReadWriteMethod()) { - return error; - } - - // Note: this metadata is only used to recover the original partition sizes. - // We do not trust the extent information, which will get rebuilt later. - auto old_metadata = ReadFromImageFile(kDsuLpMetadataFile); - if (!old_metadata) { - LOG(ERROR) << "GSI install is incomplete"; - return IGsiService::INSTALL_ERROR_GENERIC; - } - - // Recover parition information. - Image userdata_image; - if (int error = GetExistingImage(*old_metadata.get(), "userdata_gsi", &userdata_image)) { - return error; - } - partitions_.emplace(std::make_pair("userdata_gsi", std::move(userdata_image))); - - Image system_image; - if (int error = GetExistingImage(*old_metadata.get(), "system_gsi", &system_image)) { - return error; - } - partitions_.emplace(std::make_pair("system_gsi", std::move(system_image))); - - metadata_ = CreateMetadata(); - if (!metadata_) { - return IGsiService::INSTALL_ERROR_GENERIC; +int GsiInstaller::CheckInstallState() { + std::vector<std::string> gsi_images = {"system_gsi", "userdata_gsi"}; + for (const auto& image : gsi_images) { + if (!images_->PartitionExists(image) || !images_->BackingImageExists(image)) { + return IGsiService::INSTALL_ERROR_GENERIC; + } } return IGsiService::INSTALL_OK; } @@ -654,22 +363,22 @@ int GsiInstaller::ReenableGsi(bool one_shot) { return IGsiService::INSTALL_OK; } - if (int error = RebuildInstallState()) { + if (int error = CheckInstallState()) { return error; } - if (!CreateMetadataFile() || !SetBootMode(one_shot) || !CreateInstallStatusFile()) { + if (!SetBootMode(one_shot) || !CreateInstallStatusFile()) { return IGsiService::INSTALL_ERROR_GENERIC; } return IGsiService::INSTALL_OK; } int GsiInstaller::WipeUserdata() { - if (int error = RebuildInstallState()) { + if (int error = CheckInstallState()) { return error; } - auto writer = OpenPartition("userdata_gsi"); - if (!writer) { + auto device = OpenPartition("userdata_gsi"); + if (!device) { return IGsiService::INSTALL_ERROR_GENERIC; } @@ -678,9 +387,9 @@ int GsiInstaller::WipeUserdata() { static constexpr uint64_t kEraseSize = 1024 * 1024; std::string zeroes(4096, 0); - uint64_t erase_size = std::min(kEraseSize, writer->Size()); + uint64_t erase_size = std::min(kEraseSize, get_block_device_size(device->fd())); for (uint64_t i = 0; i < erase_size; i += zeroes.size()) { - if (!writer->Write(zeroes.data(), zeroes.size())) { + if (!android::base::WriteFully(device->fd(), zeroes.data(), zeroes.size())) { PLOG(ERROR) << "write userdata_gsi"; return IGsiService::INSTALL_ERROR_GENERIC; } diff --git a/gsi_installer.h b/gsi_installer.h index ece659d..9f9a94f 100644 --- a/gsi_installer.h +++ b/gsi_installer.h @@ -21,7 +21,8 @@ #include <string> #include <android/gsi/IGsiService.h> -#include <libfiemap/split_fiemap_writer.h> +#include <android/gsi/MappedImage.h> +#include <libfiemap/image_manager.h> #include <liblp/builder.h> namespace android { @@ -30,6 +31,9 @@ namespace gsi { class GsiService; class GsiInstaller final { + using ImageManager = android::fiemap::ImageManager; + using MappedDevice = android::fiemap::MappedDevice; + public: // Constructor for a new GSI installation. GsiInstaller(GsiService* service, const GsiInstallParams& params); @@ -49,78 +53,36 @@ class GsiInstaller final { // Clean up install state if gsid crashed and restarted. static void PostInstallCleanup(); - - // This helper class will redirect writes to either a SplitFiemap or - // device-mapper. - class WriteHelper { - public: - virtual ~WriteHelper(){}; - virtual bool Write(const void* data, uint64_t bytes) = 0; - virtual bool Flush() = 0; - virtual uint64_t Size() = 0; - - WriteHelper() = default; - WriteHelper(const WriteHelper&) = delete; - WriteHelper& operator=(const WriteHelper&) = delete; - WriteHelper& operator=(WriteHelper&&) = delete; - WriteHelper(WriteHelper&&) = delete; - }; + static void PostInstallCleanup(ImageManager* manager); const std::string& install_dir() const { return install_dir_; } uint64_t userdata_size() const { return userdata_size_; } private: - using MetadataBuilder = android::fs_mgr::MetadataBuilder; - using LpMetadata = android::fs_mgr::LpMetadata; - using SplitFiemap = android::fiemap::SplitFiemap; - - // The image file may be larger than the requested size, due to alignment, - // so we must track the requested size as well. - struct Image { - std::unique_ptr<SplitFiemap> writer; - uint64_t actual_size; - }; - int PerformSanityChecks(); int PreallocateFiles(); int PreallocateUserdata(); int PreallocateSystem(); - int DetermineReadWriteMethod(); bool FormatUserdata(); - bool AddPartitionFiemap(MetadataBuilder* builder, android::fs_mgr::Partition* partition, - const Image& image, const std::string& block_device); - std::unique_ptr<LpMetadata> CreateMetadata(); - std::unique_ptr<SplitFiemap> CreateFiemapWriter(const std::string& path, uint64_t size, - int* error); - std::unique_ptr<WriteHelper> OpenPartition(const std::string& name); - int GetExistingImage(const LpMetadata& metadata, const std::string& name, Image* image); - int RebuildInstallState(); + bool CreateImage(const std::string& name, uint64_t size, bool readonly); + std::unique_ptr<MappedDevice> OpenPartition(const std::string& name); + int CheckInstallState(); bool CreateInstallStatusFile(); - bool CreateMetadataFile(); bool SetBootMode(bool one_shot); - std::string GetImagePath(const std::string& name); GsiService* service_; std::string install_dir_; - std::string userdata_gsi_path_; - std::string system_gsi_path_; - uint64_t userdata_block_size_ = 0; - uint64_t system_block_size_ = 0; + std::unique_ptr<ImageManager> images_; uint64_t gsi_size_ = 0; uint64_t userdata_size_ = 0; - bool can_use_devicemapper_ = false; bool wipe_userdata_ = false; bool wipe_userdata_on_failure_ = false; // Remaining data we're waiting to receive for the GSI image. uint64_t gsi_bytes_written_ = 0; bool succeeded_ = false; - std::unique_ptr<WriteHelper> system_writer_; - - // This is used to track which GSI partitions have been created. - std::map<std::string, Image> partitions_; - std::unique_ptr<LpMetadata> metadata_; + std::unique_ptr<MappedDevice> system_device_; }; } // namespace gsi diff --git a/gsi_service.cpp b/gsi_service.cpp index dc0f0a4..89b3a74 100644 --- a/gsi_service.cpp +++ b/gsi_service.cpp @@ -34,6 +34,7 @@ #include <android-base/strings.h> #include <android/gsi/BnImageManager.h> #include <android/gsi/IGsiService.h> +#include <ext4_utils/ext4_utils.h> #include <fs_mgr.h> #include <libfiemap/image_manager.h> #include <private/android_filesystem_config.h> @@ -325,17 +326,10 @@ binder::Status GsiService::getUserdataImageSize(int64_t* _aidl_return) { } *_aidl_return = size; } else { - // Stat the size of the userdata file. - auto userdata_gsi = GetInstalledImagePath("userdata_gsi"); - struct stat s; - if (stat(userdata_gsi.c_str(), &s)) { - if (errno != ENOENT) { - PLOG(ERROR) << "open " << userdata_gsi; - return binder::Status::ok(); + if (auto manager = ImageManager::Open(kDsuMetadataDir, GetInstalledImageDir())) { + if (auto device = MappedDevice::Open(manager.get(), 10s, "userdata_gsi")) { + *_aidl_return = get_block_device_size(device->fd()); } - *_aidl_return = 0; - } else { - *_aidl_return = s.st_size; } } return binder::Status::ok(); @@ -531,8 +525,7 @@ int GsiService::ValidateInstallParams(GsiInstallParams* params) { PLOG(ERROR) << "realpath failed: " << origInstallDir; return INSTALL_ERROR_GENERIC; } - // Ensure the path ends in / for consistency. Even though GetImagePath() - // does this already, we want it to appear this way in install_dir. + // Ensure the path ends in / for consistency. if (!android::base::EndsWith(params->installDir, "/")) { params->installDir += "/"; } @@ -570,14 +563,6 @@ int GsiService::ValidateInstallParams(GsiInstallParams* params) { return INSTALL_OK; } -std::string GsiService::GetImagePath(const std::string& image_dir, const std::string& name) { - std::string dir = image_dir; - if (!android::base::EndsWith(dir, "/")) { - dir += "/"; - } - return dir + name + ".img"; -} - std::string GsiService::GetInstalledImageDir() { // If there's no install left, just return /data/gsi since that's where // installs go by default. @@ -588,10 +573,6 @@ std::string GsiService::GetInstalledImageDir() { return kDefaultDsuImageFolder; } -std::string GsiService::GetInstalledImagePath(const std::string& name) { - return GetImagePath(GetInstalledImageDir(), name); -} - int GsiService::ReenableGsi(bool one_shot) { if (!android::gsi::IsGsiInstalled()) { LOG(ERROR) << "no gsi installed - cannot re-enable"; @@ -614,24 +595,20 @@ int GsiService::ReenableGsi(bool one_shot) { bool GsiService::RemoveGsiFiles(const std::string& install_dir, bool wipeUserdata) { bool ok = true; - std::string message; - if (!SplitFiemap::RemoveSplitFiles(GetImagePath(install_dir, "system_gsi"), &message)) { - LOG(ERROR) << message; - ok = false; - } - if (wipeUserdata && - !SplitFiemap::RemoveSplitFiles(GetImagePath(install_dir, "userdata_gsi"), &message)) { - LOG(ERROR) << message; - ok = false; + if (auto manager = ImageManager::Open(kDsuMetadataDir, install_dir)) { + ok &= manager->DeleteBackingImage("system_gsi"); + if (wipeUserdata) { + ok &= manager->DeleteBackingImage("userdata_gsi"); + } } std::vector<std::string> files{ kDsuInstallStatusFile, - kDsuLpMetadataFile, kDsuOneShotBootFile, kDsuInstallDirFile, }; for (const auto& file : files) { + std::string message; if (!android::base::RemoveFileIfExists(file, &message)) { LOG(ERROR) << message; ok = false; diff --git a/gsi_service.h b/gsi_service.h index eebe140..ad582dd 100644 --- a/gsi_service.h +++ b/gsi_service.h @@ -72,24 +72,15 @@ class GsiService : public BinderService<GsiService>, public BnGsiService { static char const* getServiceName() { return kGsiServiceName; } // Helper methods for GsiInstaller. - static std::string GetImagePath(const std::string& image_dir, const std::string& name); static bool RemoveGsiFiles(const std::string& install_dir, bool wipeUserdata); bool should_abort() const { return should_abort_; } static void RunStartupTasks(); + static std::string GetInstalledImageDir(); private: friend class ImageManagerService; - using LpMetadata = android::fs_mgr::LpMetadata; - using MetadataBuilder = android::fs_mgr::MetadataBuilder; - using SplitFiemap = android::fiemap::SplitFiemap; - - struct Image { - std::unique_ptr<SplitFiemap> writer; - uint64_t actual_size; - }; - int ValidateInstallParams(GsiInstallParams* params); bool DisableGsiInstall(); int ReenableGsi(bool one_shot); @@ -97,9 +88,6 @@ class GsiService : public BinderService<GsiService>, public BnGsiService { enum class AccessLevel { System, SystemOrShell }; binder::Status CheckUid(AccessLevel level = AccessLevel::System); - static std::string GetInstalledImagePath(const std::string& name); - static std::string GetInstalledImageDir(); - std::mutex* lock() { return &lock_; } std::mutex lock_; diff --git a/libfiemap/image_manager.cpp b/libfiemap/image_manager.cpp index ca91eeb..3a060fc 100644 --- a/libfiemap/image_manager.cpp +++ b/libfiemap/image_manager.cpp @@ -87,6 +87,17 @@ bool ImageManager::IsImageMapped(const std::string& image_name) { return true; } +bool ImageManager::PartitionExists(const std::string& name) { + if (!MetadataExists(metadata_dir_)) { + return false; + } + auto metadata = OpenMetadata(metadata_dir_); + if (!metadata) { + return false; + } + return !!FindPartition(*metadata.get(), name); +} + bool ImageManager::BackingImageExists(const std::string& name) { auto header_file = GetImageHeaderPath(name); return access(header_file.c_str(), F_OK) == 0; @@ -465,5 +476,49 @@ bool ImageManager::RemoveAllImages() { return ok && RemoveAllMetadata(metadata_dir_); } +bool ImageManager::Validate() { + auto metadata = OpenMetadata(metadata_dir_); + if (!metadata) { + return false; + } + + for (const auto& partition : metadata->partitions) { + auto name = GetPartitionName(partition); + auto image_path = GetImageHeaderPath(name); + auto fiemap = SplitFiemap::Open(image_path); + if (!fiemap || !fiemap->HasPinnedExtents()) { + LOG(ERROR) << "Image is missing or was moved: " << image_path; + return false; + } + } + return true; +} + +std::unique_ptr<MappedDevice> MappedDevice::Open(ImageManager* manager, + const std::chrono::milliseconds& timeout_ms, + const std::string& name) { + std::string path; + if (!manager->MapImageDevice(name, timeout_ms, &path)) { + return nullptr; + } + + auto device = std::unique_ptr<MappedDevice>(new MappedDevice(manager, name, path)); + if (device->fd() < 0) { + return nullptr; + } + return device; +} + +MappedDevice::MappedDevice(ImageManager* manager, const std::string& name, const std::string& path) + : manager_(manager), name_(name) { + // The device is already mapped; try and open it. + fd_.reset(open(path.c_str(), O_RDWR | O_CLOEXEC)); +} + +MappedDevice::~MappedDevice() { + fd_ = {}; + manager_->UnmapImageDevice(name_); +} + } // namespace fiemap } // namespace android diff --git a/libfiemap/include/libfiemap/image_manager.h b/libfiemap/include/libfiemap/image_manager.h index 670c2ed..19c485f 100644 --- a/libfiemap/include/libfiemap/image_manager.h +++ b/libfiemap/include/libfiemap/image_manager.h @@ -23,6 +23,8 @@ #include <memory> #include <string> +#include <android-base/unique_fd.h> + namespace android { namespace fiemap { @@ -61,6 +63,14 @@ class ImageManager final { // Returns true whether the named backing image exists. bool BackingImageExists(const std::string& name); + // Returns true if the named partition exists. This does not check the + // consistency of the backing image/data file. + bool PartitionExists(const std::string& name); + + // Validates that all images still have pinned extents. This will be removed + // once b/134588268 is fixed. + bool Validate(); + private: ImageManager(const std::string& metadata_dir, const std::string& data_dir); std::string GetImageHeaderPath(const std::string& name); @@ -82,5 +92,24 @@ class ImageManager final { std::string data_dir_; }; +// RAII helper class for mapping and opening devices with an ImageManager. +class MappedDevice final { + public: + static std::unique_ptr<MappedDevice> Open(ImageManager* manager, + const std::chrono::milliseconds& timeout_ms, + const std::string& name); + + ~MappedDevice(); + + int fd() const { return fd_; } + + protected: + MappedDevice(ImageManager* manager, const std::string& name, const std::string& path); + + ImageManager* manager_; + std::string name_; + android::base::unique_fd fd_; +}; + } // namespace fiemap } // namespace android |