Merge "Add a wipe-data command to gsi_tool and IGsiService."
am: 829e252fd6

Change-Id: I819c81796f61f041cd15f56820690fbf84ba8eb7
diff --git a/Android.bp b/Android.bp
index 84653ac..4b5d229 100644
--- a/Android.bp
+++ b/Android.bp
@@ -70,6 +70,7 @@
         "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 @@
      * @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 ecb01fb..1782941 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>
@@ -65,6 +66,9 @@
 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() {
@@ -301,6 +305,7 @@
         }
         return true;
     }
+    uint64_t Size() override { return get_block_device_size(fd_); }
 
   private:
     std::string path_;
@@ -314,6 +319,7 @@
 
     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_;
@@ -608,18 +614,11 @@
     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);
@@ -645,12 +644,48 @@
     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;
+}
 
-    succeeded_ = true;
+int GsiInstaller::WipeUserdata() {
+    if (int error = RebuildInstallState()) {
+        return error;
+    }
+
+    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 @@
     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 @@
         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 @@
                                                     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 @@
     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 @@
     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 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 @@
         {"enable", Enable},
         {"install", Install},
         {"wipe", Wipe},
+        {"wipe-data", WipeData},
         {"status", Status},
         {"cancel", Cancel},
         // clang-format on
@@ -333,6 +335,43 @@
     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 @@
             "               --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]);