diff options
author | David Anderson <dvander@google.com> | 2019-06-11 16:43:24 -0700 |
---|---|---|
committer | David Anderson <dvander@google.com> | 2019-06-11 16:44:57 -0700 |
commit | 8bdf6257ff951be4769eb583b02fd1a239580a71 (patch) | |
tree | 7510fddae32b1a2303d8c18eae215afcd91692ae | |
parent | d013ed7eeae0d4c1d00f7316d7eb1f37404bb430 (diff) | |
download | gsid-8bdf6257ff951be4769eb583b02fd1a239580a71.tar.gz |
Add a wipe-data command to gsi_tool and IGsiService.
Bug: 134185850
Test: gsi_tool install
reboot; skip setup wizard
reboot
gsi_tool wipe-data
gsi_tool enable
reboot; expect setup wizard
Change-Id: I44b676f9e08a890b14f056c7ab095c42158d9eb4
-rw-r--r-- | Android.bp | 1 | ||||
-rw-r--r-- | aidl/android/gsi/IGsiService.aidl | 9 | ||||
-rw-r--r-- | gsi_installer.cpp | 53 | ||||
-rw-r--r-- | gsi_installer.h | 5 | ||||
-rw-r--r-- | gsi_service.cpp | 15 | ||||
-rw-r--r-- | gsi_service.h | 1 | ||||
-rw-r--r-- | gsi_tool.cpp | 40 |
7 files changed, 114 insertions, 10 deletions
@@ -69,6 +69,7 @@ cc_binary { "gsi_aidl_interface-cpp", "libbase", "libbinder", + "libext4_utils", "libfs_mgr", "libgsi", "liblog", diff --git a/aidl/android/gsi/IGsiService.aidl b/aidl/android/gsi/IGsiService.aidl index 8a0201d..4ffdf62 100644 --- a/aidl/android/gsi/IGsiService.aidl +++ b/aidl/android/gsi/IGsiService.aidl @@ -172,4 +172,13 @@ interface IGsiService { * @return 0 on success, an error code on failure. */ int beginGsiInstall(in GsiInstallParams params); + + /** + * Wipe the userdata of an existing GSI install. This will not work if the + * GSI is currently running. The userdata image will not be removed, but the + * first block will be zeroed ensuring that the next GSI boot formats /data. + * + * @return 0 on success, an error code on failure. + */ + int wipeGsiUserdata(); } diff --git a/gsi_installer.cpp b/gsi_installer.cpp index 93d7d53..ce996a5 100644 --- a/gsi_installer.cpp +++ b/gsi_installer.cpp @@ -21,6 +21,7 @@ #include <android-base/file.h> #include <android-base/logging.h> #include <android-base/unique_fd.h> +#include <ext4_utils/ext4_utils.h> #include <fs_mgr_dm_linear.h> #include <libdm/dm.h> #include <libgsi/libgsi.h> @@ -64,6 +65,9 @@ GsiInstaller::GsiInstaller(GsiService* service, const GsiInstallParams& params) GsiInstaller::GsiInstaller(GsiService* service, const std::string& install_dir) : service_(service), install_dir_(install_dir) { system_gsi_path_ = GetImagePath("system_gsi"); + + // The install already exists, so always mark it as succeeded. + succeeded_ = true; } GsiInstaller::~GsiInstaller() { @@ -300,6 +304,7 @@ class FdWriter final : public GsiInstaller::WriteHelper { } return true; } + uint64_t Size() override { return get_block_device_size(fd_); } private: std::string path_; @@ -313,6 +318,7 @@ class SplitFiemapWriter final : public GsiInstaller::WriteHelper { 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_; @@ -601,18 +607,11 @@ int GsiInstaller::SetGsiBootable(bool one_shot) { return IGsiService::INSTALL_OK; } -int GsiInstaller::ReenableGsi(bool one_shot) { +int GsiInstaller::RebuildInstallState() { if (int error = DetermineReadWriteMethod()) { return error; } - if (IsGsiRunning()) { - if (!SetBootMode(one_shot) || !CreateInstallStatusFile()) { - return IGsiService::INSTALL_ERROR_GENERIC; - } - return IGsiService::INSTALL_OK; - } - // 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(kGsiLpMetadataFile); @@ -638,12 +637,48 @@ int GsiInstaller::ReenableGsi(bool one_shot) { if (!metadata_) { return IGsiService::INSTALL_ERROR_GENERIC; } + return IGsiService::INSTALL_OK; +} + +int GsiInstaller::ReenableGsi(bool one_shot) { + if (IsGsiRunning()) { + if (!SetBootMode(one_shot) || !CreateInstallStatusFile()) { + return IGsiService::INSTALL_ERROR_GENERIC; + } + return IGsiService::INSTALL_OK; + } + if (int error = RebuildInstallState()) { + return error; + } if (!CreateMetadataFile() || !SetBootMode(one_shot) || !CreateInstallStatusFile()) { return IGsiService::INSTALL_ERROR_GENERIC; } + return IGsiService::INSTALL_OK; +} + +int GsiInstaller::WipeUserdata() { + if (int error = RebuildInstallState()) { + return error; + } - succeeded_ = true; + auto writer = OpenPartition("userdata_gsi"); + if (!writer) { + return IGsiService::INSTALL_ERROR_GENERIC; + } + + // Wipe the first 1MiB of the device, ensuring both the first block and + // the superblock are destroyed. + static constexpr uint64_t kEraseSize = 1024 * 1024; + + std::string zeroes(4096, 0); + uint64_t erase_size = std::min(kEraseSize, writer->Size()); + for (uint64_t i = 0; i < erase_size; i += zeroes.size()) { + if (!writer->Write(zeroes.data(), zeroes.size())) { + PLOG(ERROR) << "write userdata_gsi"; + return IGsiService::INSTALL_ERROR_GENERIC; + } + } return IGsiService::INSTALL_OK; } diff --git a/gsi_installer.h b/gsi_installer.h index 51c2bd6..a1df3ed 100644 --- a/gsi_installer.h +++ b/gsi_installer.h @@ -43,8 +43,9 @@ class GsiInstaller final { bool CommitGsiChunk(const void* data, size_t bytes); int SetGsiBootable(bool one_shot); - // Methods for re-enabling an existing install. + // Methods for interacting with an existing install. int ReenableGsi(bool one_shot); + int WipeUserdata(); // Clean up install state if gsid crashed and restarted. static void PostInstallCleanup(); @@ -56,6 +57,7 @@ class GsiInstaller final { 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; @@ -92,6 +94,7 @@ class GsiInstaller final { 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 CreateInstallStatusFile(); bool CreateMetadataFile(); bool SetBootMode(bool one_shot); diff --git a/gsi_service.cpp b/gsi_service.cpp index 4dc703f..5f51cd8 100644 --- a/gsi_service.cpp +++ b/gsi_service.cpp @@ -349,6 +349,21 @@ binder::Status GsiService::getInstalledGsiImageDir(std::string* _aidl_return) { return binder::Status::ok(); } +binder::Status GsiService::wipeGsiUserdata(int* _aidl_return) { + ENFORCE_SYSTEM_OR_SHELL; + std::lock_guard<std::mutex> guard(main_lock_); + + if (IsGsiRunning() || !IsGsiInstalled()) { + *_aidl_return = IGsiService::INSTALL_ERROR_GENERIC; + return binder::Status::ok(); + } + + auto installer = std::make_unique<GsiInstaller>(this, GetInstalledImageDir()); + *_aidl_return = installer->WipeUserdata(); + + return binder::Status::ok(); +} + binder::Status GsiService::CheckUid(AccessLevel level) { std::vector<uid_t> allowed_uids{AID_ROOT, AID_SYSTEM}; if (level == AccessLevel::SystemOrShell) { diff --git a/gsi_service.h b/gsi_service.h index ddc6fc4..1607843 100644 --- a/gsi_service.h +++ b/gsi_service.h @@ -60,6 +60,7 @@ class GsiService : public BinderService<GsiService>, public BnGsiService { binder::Status getUserdataImageSize(int64_t* _aidl_return) override; binder::Status getGsiBootStatus(int* _aidl_return) override; binder::Status getInstalledGsiImageDir(std::string* _aidl_return) override; + binder::Status wipeGsiUserdata(int* _aidl_return) override; // This is in GsiService, rather than GsiInstaller, since we need to access // it outside of the main lock which protects the unique_ptr. diff --git a/gsi_tool.cpp b/gsi_tool.cpp index f0c5d50..f5f549f 100644 --- a/gsi_tool.cpp +++ b/gsi_tool.cpp @@ -45,6 +45,7 @@ static int Disable(sp<IGsiService> gsid, int argc, char** argv); static int Enable(sp<IGsiService> gsid, int argc, char** argv); static int Install(sp<IGsiService> gsid, int argc, char** argv); static int Wipe(sp<IGsiService> gsid, int argc, char** argv); +static int WipeData(sp<IGsiService> gsid, int argc, char** argv); static int Status(sp<IGsiService> gsid, int argc, char** argv); static int Cancel(sp<IGsiService> gsid, int argc, char** argv); @@ -54,6 +55,7 @@ static const std::map<std::string, CommandCallback> kCommandMap = { {"enable", Enable}, {"install", Install}, {"wipe", Wipe}, + {"wipe-data", WipeData}, {"status", Status}, {"cancel", Cancel}, // clang-format on @@ -333,6 +335,43 @@ static int Wipe(sp<IGsiService> gsid, int argc, char** /* argv */) { return 0; } +static int WipeData(sp<IGsiService> gsid, int argc, char** /* argv */) { + if (argc > 1) { + std::cerr << "Unrecognized arguments to wipe-data.\n"; + return EX_USAGE; + } + + bool running; + auto status = gsid->isGsiRunning(&running); + if (!status.isOk()) { + std::cerr << "error: " << status.exceptionMessage().string() << std::endl; + return EX_SOFTWARE; + } + if (running) { + std::cerr << "Cannot wipe GSI userdata while running a GSI.\n"; + return EX_USAGE; + } + + bool installed; + status = gsid->isGsiInstalled(&installed); + if (!status.isOk()) { + std::cerr << "error: " << status.exceptionMessage().string() << std::endl; + return EX_SOFTWARE; + } + if (!installed) { + std::cerr << "No GSI is installed.\n"; + return EX_USAGE; + } + + int error; + status = gsid->wipeGsiUserdata(&error); + if (!status.isOk() || error) { + std::cerr << "Could not wipe GSI userdata: " << ErrorMessage(status, error) << "\n"; + return EX_SOFTWARE; + } + return 0; +} + static int Status(sp<IGsiService> gsid, int argc, char** /* argv */) { if (argc > 1) { std::cerr << "Unrecognized arguments to status." << std::endl; @@ -462,6 +501,7 @@ static int usage(int /* argc */, char* argv[]) { " --userdata-size (the latter defaults to 8GiB)\n" " --wipe (remove old gsi userdata first)\n" " wipe Completely remove a GSI and its associated data\n" + " wipe-data Ensure the GSI's userdata will be formatted\n" " cancel Cancel the installation\n" " status Show status\n", argv[0], argv[0]); |