Do not hardcode /data/gsi in GsiService.

This patch removes hardcoding of /data/gsi. Instead, StartInstall is
given an installation directory, and this is saved in
/metadata/gsi/install_dir. When wiping or re-enabling a GSI, image paths
are recovered from install_dir.

This does not actually make the path configurable: it is a refactoring
to no longer rely on hardcoded image paths.

Included are some minor cleanups to ensure that cancelled installs
delete files properly.

Bug: 126230649
Test: gsi_tool install, enable, wipe
Change-Id: Ieb788e84d19127e8e3375909f3c522e16462d384
diff --git a/aidl/android/gsi/IGsiService.aidl b/aidl/android/gsi/IGsiService.aidl
index 7cd8408..9c909c4 100644
--- a/aidl/android/gsi/IGsiService.aidl
+++ b/aidl/android/gsi/IGsiService.aidl
@@ -149,4 +149,10 @@
      * currently running, DISABLED or SINGLE_BOOT can still be returned.
      */
     int getGsiBootStatus();
+
+    /**
+     * If a GSI is installed, returns the directory where the installed images
+     * are located. Otherwise, returns an empty string.
+     */
+     @utf8InCpp String getInstalledGsiImageDir();
 }
diff --git a/file_paths.h b/file_paths.h
index 0fc255e..a1d7d30 100644
--- a/file_paths.h
+++ b/file_paths.h
@@ -19,14 +19,12 @@
 namespace android {
 namespace gsi {
 
-// TODO(b/122671850) change paths
 static constexpr char kGsiDataFolder[] = "/data/gsi";
-static constexpr char kUserdataFile[] = "/data/gsi/userdata_gsi.img";
-static constexpr char kSystemFile[] = "/data/gsi/system_gsi.img";
 
 static constexpr char kGsiMetadataFolder[] = "/metadata/gsi";
 static constexpr char kGsiLpMetadataFile[] = "/metadata/gsi/lp_metadata";
 static constexpr char kGsiOneShotBootFile[] = "/metadata/gsi/one_shot_boot";
+static constexpr char kGsiInstallDirFile[] = "/metadata/gsi/install_dir";
 
 // This file can contain the following values:
 //   [int]      - boot attempt counter, starting from 0
diff --git a/gsi_service.cpp b/gsi_service.cpp
index 533af34..35194a2 100644
--- a/gsi_service.cpp
+++ b/gsi_service.cpp
@@ -31,6 +31,7 @@
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
+#include <android-base/strings.h>
 #include <android/gsi/IGsiService.h>
 #include <fs_mgr_dm_linear.h>
 #include <libdm/dm.h>
@@ -98,14 +99,11 @@
     // Make sure any interrupted installations are cleaned up.
     PostInstallCleanup();
 
-    // Only rm userdata_gsi if one didn't already exist.
-    wipe_userdata_on_failure_ = wipeUserdata || access(kUserdataFile, F_OK);
-
-    int status = StartInstall(gsiSize, userdataSize, wipeUserdata);
+    int status = StartInstall(kGsiDataFolder, gsiSize, userdataSize, wipeUserdata);
     if (status != INSTALL_OK) {
         // Perform local cleanup and delete any lingering files.
         PostInstallCleanup();
-        RemoveGsiFiles(wipe_userdata_on_failure_);
+        RemoveGsiFiles(kGsiDataFolder, wipe_userdata_on_failure_);
     }
     *_aidl_return = status;
 
@@ -168,13 +166,18 @@
 
     if (installing_) {
         ENFORCE_SYSTEM;
-        *_aidl_return = SetGsiBootable(one_shot);
+        if (int error = SetGsiBootable(one_shot)) {
+            PostInstallCleanup();
+            RemoveGsiFiles(install_dir_, wipe_userdata_on_failure_);
+            *_aidl_return = error;
+        } else {
+            *_aidl_return = INSTALL_OK;
+        }
     } else {
         ENFORCE_SYSTEM_OR_SHELL;
         *_aidl_return = ReenableGsi(one_shot);
     }
 
-    PostInstallCleanup();
     return binder::Status::ok();
 }
 
@@ -183,13 +186,19 @@
     std::lock_guard<std::mutex> guard(main_lock_);
 
     // Just in case an install was left hanging.
-    PostInstallCleanup();
+    std::string install_dir;
+    if (installing_) {
+        install_dir = install_dir_;
+        PostInstallCleanup();
+    } else {
+        install_dir = GetInstalledImageDir();
+    }
 
     if (IsGsiRunning()) {
         // Can't remove gsi files while running.
         *_aidl_return = UninstallGsi();
     } else {
-        *_aidl_return = RemoveGsiFiles(true /* wipeUserdata */);
+        *_aidl_return = RemoveGsiFiles(install_dir, true /* wipeUserdata */);
     }
     return binder::Status::ok();
 }
@@ -237,7 +246,7 @@
     }
 
     PostInstallCleanup();
-    RemoveGsiFiles(wipe_userdata_on_failure_);
+    RemoveGsiFiles(install_dir_, wipe_userdata_on_failure_);
 
     *_aidl_return = true;
     return binder::Status::ok();
@@ -284,6 +293,8 @@
 
 binder::Status GsiService::getUserdataImageSize(int64_t* _aidl_return) {
     ENFORCE_SYSTEM;
+    std::lock_guard<std::mutex> guard(main_lock_);
+
     *_aidl_return = -1;
 
     if (installing_) {
@@ -305,10 +316,11 @@
         *_aidl_return = size;
     } else {
         // Stat the size of the userdata file.
+        auto userdata_gsi = GetInstalledImagePath("userdata_gsi");
         struct stat s;
-        if (stat(kUserdataFile, &s)) {
+        if (stat(userdata_gsi.c_str(), &s)) {
             if (errno != ENOENT) {
-                PLOG(ERROR) << "open " << kUserdataFile;
+                PLOG(ERROR) << "open " << userdata_gsi;
                 return binder::Status::ok();
             }
             *_aidl_return = 0;
@@ -319,6 +331,16 @@
     return binder::Status::ok();
 }
 
+binder::Status GsiService::getInstalledGsiImageDir(std::string* _aidl_return) {
+    ENFORCE_SYSTEM;
+    std::lock_guard<std::mutex> guard(main_lock_);
+
+    if (IsGsiInstalled()) {
+        *_aidl_return = GetInstalledImageDir();
+    }
+    return binder::Status::ok();
+}
+
 binder::Status GsiService::CheckUid(AccessLevel level) {
     std::vector<uid_t> allowed_uids{AID_ROOT, AID_SYSTEM};
     if (level == AccessLevel::SystemOrShell) {
@@ -353,7 +375,9 @@
     partitions_ .clear();
 }
 
-int GsiService::StartInstall(int64_t gsi_size, int64_t userdata_size, bool wipe_userdata) {
+int GsiService::StartInstall(const std::string& install_dir, int64_t gsi_size,
+                             int64_t userdata_size, bool wipe_userdata) {
+    installing_ = true;
     userdata_block_size_ = 0;
     system_block_size_ = 0;
     gsi_size_ = gsi_size;
@@ -362,6 +386,17 @@
     can_use_devicemapper_ = false;
     gsi_bytes_written_ = 0;
 
+    // Ensure the path ends in / for consistency.
+    install_dir_ = install_dir;
+    if (!android::base::EndsWith(install_dir_, "/")) {
+        install_dir_ += "/";
+    }
+    userdata_gsi_path_ = GetImagePath(install_dir_, "userdata_gsi");
+    system_gsi_path_ = GetImagePath(install_dir_, "system_gsi");
+
+    // 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 (gsi_size % LP_SECTOR_SIZE) {
         LOG(ERROR) << "GSI size " << gsi_size << " is not a multiple of " << LP_SECTOR_SIZE;
         return INSTALL_ERROR_GENERIC;
@@ -392,7 +427,8 @@
     // 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(kSystemFile, &block_device, &can_use_devicemapper_)) {
+    if (!FiemapWriter::GetBlockDeviceForFile(system_gsi_path_.c_str(), &block_device,
+                                             &can_use_devicemapper_)) {
         return INSTALL_ERROR_GENERIC;
     }
 
@@ -401,11 +437,31 @@
     if (!system_writer_) {
         return INSTALL_ERROR_GENERIC;
     }
-
-    installing_ = true;
     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.
+    std::string dir;
+    if (android::base::ReadFileToString(kGsiInstallDirFile, &dir)) {
+        return dir;
+    }
+    return kGsiDataFolder;
+}
+
+std::string GsiService::GetInstalledImagePath(const std::string& name) {
+    return GetImagePath(GetInstalledImageDir(), name);
+}
+
 int GsiService::PerformSanityChecks() {
     if (gsi_size_ < 0) {
         LOG(ERROR) << "image size " << gsi_size_ << " is negative";
@@ -417,7 +473,7 @@
     }
 
     struct statvfs sb;
-    if (statvfs(kGsiDataFolder, &sb)) {
+    if (statvfs(install_dir_.c_str(), &sb)) {
         PLOG(ERROR) << "failed to read file system stats";
         return INSTALL_ERROR_GENERIC;
     }
@@ -443,9 +499,9 @@
 
 int GsiService::PreallocateFiles() {
     if (wipe_userdata_) {
-        SplitFiemap::RemoveSplitFiles(kUserdataFile);
+        SplitFiemap::RemoveSplitFiles(userdata_gsi_path_);
     }
-    SplitFiemap::RemoveSplitFiles(kSystemFile);
+    SplitFiemap::RemoveSplitFiles(system_gsi_path_);
 
     // TODO: trigger GC from fiemap writer.
 
@@ -470,23 +526,23 @@
 int GsiService::PreallocateUserdata() {
     int error;
     std::unique_ptr<SplitFiemap> userdata_image;
-    if (wipe_userdata_ || access(kUserdataFile, F_OK)) {
+    if (wipe_userdata_ || access(userdata_gsi_path_.c_str(), F_OK)) {
         if (!userdata_size_) {
             userdata_size_ = kDefaultUserdataSize;
         }
 
         StartAsyncOperation("create userdata", userdata_size_);
-        userdata_image = CreateFiemapWriter(kUserdataFile, userdata_size_, &error);
+        userdata_image = CreateFiemapWriter(userdata_gsi_path_, userdata_size_, &error);
         if (!userdata_image) {
-            LOG(ERROR) << "Could not create userdata image: " << kUserdataFile;
+            LOG(ERROR) << "Could not create userdata image: " << userdata_gsi_path_;
             return error;
         }
         // Signal that we need to reformat userdata.
         wipe_userdata_ = true;
     } else {
-        userdata_image = CreateFiemapWriter(kUserdataFile, 0, &error);
+        userdata_image = CreateFiemapWriter(userdata_gsi_path_, 0, &error);
         if (!userdata_image) {
-            LOG(ERROR) << "Could not open userdata image: " << kUserdataFile;
+            LOG(ERROR) << "Could not open userdata image: " << userdata_gsi_path_;
             return error;
         }
         if (userdata_size_ && userdata_image->size() < userdata_size_) {
@@ -509,7 +565,7 @@
     StartAsyncOperation("create system", gsi_size_);
 
     int error;
-    auto system_image = CreateFiemapWriter(kSystemFile, gsi_size_, &error);
+    auto system_image = CreateFiemapWriter(system_gsi_path_, gsi_size_, &error);
     if (!system_image) {
         return error;
     }
@@ -613,7 +669,7 @@
         if (fd < 0) {
             PLOG(ERROR) << "could not open " << path;
         }
-        return std::make_unique<FdWriter>(kUserdataFile, std::move(fd));
+        return std::make_unique<FdWriter>(GetImagePath(install_dir_, name), std::move(fd));
     }
 
     auto iter = partitions_.find(name);
@@ -706,6 +762,12 @@
         }
     }
 
+    // Remember the installation directory.
+    if (!android::base::WriteStringToFile(install_dir_, kGsiInstallDirFile)) {
+        PLOG(ERROR) << "write failed: " << kGsiInstallDirFile;
+        return INSTALL_ERROR_GENERIC;
+    }
+
     // Note: create the install status file last, since this is the actual boot
     // indicator.
     if (!CreateMetadataFile(*metadata_.get()) || !SetBootMode(one_shot) ||
@@ -785,7 +847,7 @@
 int GsiService::GetExistingImage(const LpMetadata& metadata, const std::string& name,
                                  Image* image) {
     int error;
-    std::string path = kGsiDataFolder + "/"s + name + ".img"s;
+    std::string path = GetInstalledImagePath(name);
     auto writer = CreateFiemapWriter(path.c_str(), 0, &error);
     if (!writer) {
         return error;
@@ -803,14 +865,15 @@
     return INSTALL_OK;
 }
 
-bool GsiService::RemoveGsiFiles(bool wipeUserdata) {
+bool GsiService::RemoveGsiFiles(const std::string& install_dir, bool wipeUserdata) {
     bool ok = true;
     std::string message;
-    if (!SplitFiemap::RemoveSplitFiles(kSystemFile, &message)) {
+    if (!SplitFiemap::RemoveSplitFiles(GetImagePath(install_dir, "system_gsi"), &message)) {
         LOG(ERROR) << message;
         ok = false;
     }
-    if (wipeUserdata && !SplitFiemap::RemoveSplitFiles(kUserdataFile, &message)) {
+    if (wipeUserdata &&
+        !SplitFiemap::RemoveSplitFiles(GetImagePath(install_dir, "userdata_gsi"), &message)) {
         LOG(ERROR) << message;
         ok = false;
     }
@@ -819,6 +882,7 @@
             kGsiInstallStatusFile,
             kGsiLpMetadataFile,
             kGsiOneShotBootFile,
+            kGsiInstallDirFile,
     };
     for (const auto& file : files) {
         if (!android::base::RemoveFileIfExists(file, &message)) {
@@ -979,7 +1043,7 @@
     if (!IsGsiRunning()) {
         // Check if a wipe was requested from fastboot or adb-in-gsi.
         if (boot_key == kInstallStatusWipe) {
-            RemoveGsiFiles(true /* wipeUserdata */);
+            RemoveGsiFiles(GetInstalledImageDir(), true /* wipeUserdata */);
         }
     } else {
         // NB: When single-boot is enabled, init will write "disabled" into the
diff --git a/gsi_service.h b/gsi_service.h
index 46006c0..b563e41 100644
--- a/gsi_service.h
+++ b/gsi_service.h
@@ -55,6 +55,7 @@
     binder::Status isGsiInstallInProgress(bool* _aidl_return) override;
     binder::Status getUserdataImageSize(int64_t* _aidl_return) override;
     binder::Status getGsiBootStatus(int* _aidl_return) override;
+    binder::Status getInstalledGsiImageDir(std::string* _aidl_return) override;
 
     static char const* getServiceName() { return kGsiServiceName; }
 
@@ -85,7 +86,8 @@
         uint64_t actual_size;
     };
 
-    int StartInstall(int64_t gsi_size, int64_t userdata_size, bool wipe_userdata);
+    int StartInstall(const std::string& install_dir, int64_t gsi_size, int64_t userdata_size,
+                     bool wipe_userdata);
     int PerformSanityChecks();
     int PreallocateFiles();
     int PreallocateUserdata();
@@ -117,7 +119,10 @@
     };
     binder::Status CheckUid(AccessLevel level = AccessLevel::System);
 
-    static bool RemoveGsiFiles(bool wipeUserdata);
+    static bool RemoveGsiFiles(const std::string& install_dir, bool wipeUserdata);
+    static std::string GetImagePath(const std::string& image_dir, const std::string& name);
+    static std::string GetInstalledImagePath(const std::string& name);
+    static std::string GetInstalledImageDir();
 
     std::mutex main_lock_;
 
@@ -127,6 +132,9 @@
 
     // These are initialized or set in StartInstall().
     bool installing_ = false;
+    std::string install_dir_;
+    std::string userdata_gsi_path_;
+    std::string system_gsi_path_;
     uint64_t userdata_block_size_;
     uint64_t system_block_size_;
     uint64_t gsi_size_;
diff --git a/gsi_tool.cpp b/gsi_tool.cpp
index 3403cd1..e394371 100644
--- a/gsi_tool.cpp
+++ b/gsi_tool.cpp
@@ -312,7 +312,13 @@
         std::cerr << "Could not remove GSI install: " << ErrorMessage(status) << "\n";
         return EX_SOFTWARE;
     }
-    std::cout << "Live image install successfully removed." << std::endl;
+
+    bool running = false;
+    if (gsid->isGsiRunning(&running).isOk() && running) {
+        std::cout << "Live image install will be removed next reboot." << std::endl;
+    } else {
+        std::cout << "Live image install successfully removed." << std::endl;
+    }
     return 0;
 }