Merge "Support ZSTD in userspace COW"
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index 844357c..3b8866e 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -459,6 +459,8 @@
     {"reboot,sys_ldo_ok,pmic,main", 227},
     {"reboot,sys_ldo_ok,pmic,sub", 228},
     {"reboot,smpl_timeout,pmic,main", 229},
+    {"reboot,ota,.*", 230},
+    {"reboot,periodic,.*", 231},
 };
 
 // Converts a string value representing the reason the system booted to an
diff --git a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
index 24c91a8..f45d4ed 100644
--- a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
+++ b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
@@ -214,29 +214,6 @@
 // Get partition size from update package metadata.
 uint64_t GetSize(PartitionUpdate* partition_update);
 
-// Util class for test cases on low space scenario. These tests assumes image manager
-// uses /data as backup device.
-class LowSpaceUserdata {
-  public:
-    // Set the maximum free space allowed for this test. If /userdata has more space than the given
-    // number, a file is allocated to consume space.
-    AssertionResult Init(uint64_t max_free_space);
-
-    uint64_t free_space() const;
-    uint64_t available_space() const;
-    uint64_t bsize() const;
-
-  private:
-    AssertionResult ReadUserdataStats();
-
-    static constexpr const char* kUserDataDevice = "/data";
-    std::unique_ptr<TemporaryFile> big_file_;
-    bool initialized_ = false;
-    uint64_t free_space_ = 0;
-    uint64_t available_space_ = 0;
-    uint64_t bsize_ = 0;
-};
-
 bool IsVirtualAbEnabled();
 
 #define SKIP_IF_NON_VIRTUAL_AB()                                                        \
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 64637c2..2661482 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -3133,6 +3133,7 @@
     for (auto&& [name, status] : all_snapshot_status) {
         sum += status.cow_file_size();
     }
+    LOG(INFO) << "Calculated needed COW space: " << sum << " bytes";
     return Return::NoSpace(sum);
 }
 
@@ -3279,7 +3280,10 @@
 
     auto ret = CreateUpdateSnapshotsInternal(lock.get(), manifest, &cow_creator, &created_devices,
                                              &all_snapshot_status);
-    if (!ret.is_ok()) return ret;
+    if (!ret.is_ok()) {
+        LOG(ERROR) << "CreateUpdateSnapshotsInternal failed: " << ret.string();
+        return ret;
+    }
 
     auto exported_target_metadata = target_metadata->Export();
     if (exported_target_metadata == nullptr) {
@@ -3496,7 +3500,10 @@
         // Create the backing COW image if necessary.
         if (snapshot_status.cow_file_size() > 0) {
             auto ret = CreateCowImage(lock, name);
-            if (!ret.is_ok()) return AddRequiredSpace(ret, *all_snapshot_status);
+            if (!ret.is_ok()) {
+                LOG(ERROR) << "CreateCowImage failed: " << ret.string();
+                return AddRequiredSpace(ret, *all_snapshot_status);
+            }
         }
 
         LOG(INFO) << "Successfully created snapshot for " << name;
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 1fbfaf7..22731e7 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -19,6 +19,7 @@
 #include <signal.h>
 #include <sys/file.h>
 #include <sys/stat.h>
+#include <sys/statvfs.h>
 #include <sys/types.h>
 
 #include <chrono>
@@ -2371,20 +2372,44 @@
             << "FinishedSnapshotWrites should detect overflow of CoW device.";
 }
 
-TEST_F(SnapshotUpdateTest, LowSpace) {
-    static constexpr auto kMaxFree = 10_MiB;
-    auto userdata = std::make_unique<LowSpaceUserdata>();
-    ASSERT_TRUE(userdata->Init(kMaxFree));
+// Get max file size and free space.
+std::pair<uint64_t, uint64_t> GetBigFileLimit() {
+    struct statvfs fs;
+    if (statvfs("/data", &fs) < 0) {
+        PLOG(ERROR) << "statfs failed";
+        return {0, 0};
+    }
 
-    // Grow all partitions to 10_MiB, total 30_MiB. This requires 30 MiB of CoW space. After
-    // using the empty space in super (< 1 MiB), it uses 30 MiB of /userdata space.
+    auto fs_limit = static_cast<uint64_t>(fs.f_blocks) * (fs.f_bsize - 1);
+    auto fs_free = static_cast<uint64_t>(fs.f_bfree) * fs.f_bsize;
+
+    LOG(INFO) << "Big file limit: " << fs_limit << ", free space: " << fs_free;
+
+    return {fs_limit, fs_free};
+}
+
+TEST_F(SnapshotUpdateTest, LowSpace) {
+    // To make the low space test more reliable, we force a large cow estimate.
+    // However legacy VAB ignores the COW estimate and uses InstallOperations
+    // to compute the exact size required for dm-snapshot. It's difficult to
+    // make this work reliably (we'd need to somehow fake an extremely large
+    // super partition, and we don't have that level of dependency injection).
+    //
+    // For now, just skip this test on legacy VAB.
+    if (!snapuserd_required_) {
+        GTEST_SKIP() << "Skipping test on legacy VAB";
+    }
+
+    auto fs = GetBigFileLimit();
+    ASSERT_NE(fs.first, 0);
+
     constexpr uint64_t partition_size = 10_MiB;
     SetSize(sys_, partition_size);
     SetSize(vnd_, partition_size);
     SetSize(prd_, partition_size);
-    sys_->set_estimate_cow_size(partition_size);
-    vnd_->set_estimate_cow_size(partition_size);
-    prd_->set_estimate_cow_size(partition_size);
+    sys_->set_estimate_cow_size(fs.first);
+    vnd_->set_estimate_cow_size(fs.first);
+    prd_->set_estimate_cow_size(fs.first);
 
     AddOperationForPartitions();
 
@@ -2393,8 +2418,12 @@
     auto res = sm->CreateUpdateSnapshots(manifest_);
     ASSERT_FALSE(res);
     ASSERT_EQ(Return::ErrorCode::NO_SPACE, res.error_code());
-    ASSERT_GE(res.required_size(), 14_MiB);
-    ASSERT_LT(res.required_size(), 40_MiB);
+
+    // It's hard to predict exactly how much free space is needed, since /data
+    // is writable and the test is not the only process running. Divide by two
+    // as a rough lower bound, and adjust this in the future as necessary.
+    auto expected_delta = fs.first - fs.second;
+    ASSERT_GE(res.required_size(), expected_delta / 2);
 }
 
 TEST_F(SnapshotUpdateTest, AddPartition) {
@@ -2776,6 +2805,7 @@
     void TearDown() override {
         RETURN_IF_NON_VIRTUAL_AB();
         CleanUp();
+        SnapshotTest::TearDown();
     }
     void CleanUp() {
         if (!image_manager_) {
@@ -2789,26 +2819,13 @@
 };
 
 TEST_F(ImageManagerTest, CreateImageNoSpace) {
-    bool at_least_one_failure = false;
-    for (uint64_t size = 1_MiB; size <= 512_MiB; size *= 2) {
-        auto userdata = std::make_unique<LowSpaceUserdata>();
-        ASSERT_TRUE(userdata->Init(size));
+    auto fs = GetBigFileLimit();
+    ASSERT_NE(fs.first, 0);
 
-        uint64_t to_allocate = userdata->free_space() + userdata->bsize();
-
-        auto res = image_manager_->CreateBackingImage(kImageName, to_allocate,
-                                                      IImageManager::CREATE_IMAGE_DEFAULT);
-        if (!res) {
-            at_least_one_failure = true;
-        } else {
-            ASSERT_EQ(res.error_code(), FiemapStatus::ErrorCode::NO_SPACE) << res.string();
-        }
-
-        CleanUp();
-    }
-
-    ASSERT_TRUE(at_least_one_failure)
-            << "We should have failed to allocate at least one over-sized image";
+    auto res = image_manager_->CreateBackingImage(kImageName, fs.first,
+                                                  IImageManager::CREATE_IMAGE_DEFAULT);
+    ASSERT_FALSE(res);
+    ASSERT_EQ(res.error_code(), FiemapStatus::ErrorCode::NO_SPACE) << res.string();
 }
 
 bool Mkdir(const std::string& path) {
diff --git a/fs_mgr/libsnapshot/test_helpers.cpp b/fs_mgr/libsnapshot/test_helpers.cpp
index 9f1d676..a224f6b 100644
--- a/fs_mgr/libsnapshot/test_helpers.cpp
+++ b/fs_mgr/libsnapshot/test_helpers.cpp
@@ -214,68 +214,6 @@
     return partition_update->mutable_new_partition_info()->size();
 }
 
-AssertionResult LowSpaceUserdata::Init(uint64_t max_free_space) {
-    auto res = ReadUserdataStats();
-    if (!res) return res;
-
-    // Try to fill up the disk as much as possible until free_space_ <= max_free_space.
-    big_file_ = std::make_unique<TemporaryFile>();
-    if (big_file_->fd == -1) {
-        return AssertionFailure() << strerror(errno);
-    }
-    if (!android::base::StartsWith(big_file_->path, kUserDataDevice)) {
-        return AssertionFailure() << "Temp file allocated to " << big_file_->path << ", not in "
-                                  << kUserDataDevice;
-    }
-    uint64_t next_consume = std::min(std::max(available_space_, max_free_space) - max_free_space,
-                                     (uint64_t)std::numeric_limits<off_t>::max());
-    off_t allocated = 0;
-    while (next_consume > 0 && free_space_ > max_free_space) {
-        int status = fallocate(big_file_->fd, 0, allocated, next_consume);
-        if (status == -1 && errno == ENOSPC) {
-            next_consume /= 2;
-            continue;
-        }
-        if (status == -1) {
-            return AssertionFailure() << strerror(errno);
-        }
-        allocated += next_consume;
-
-        res = ReadUserdataStats();
-        if (!res) return res;
-    }
-
-    LOG(INFO) << allocated << " bytes allocated to " << big_file_->path;
-    initialized_ = true;
-    return AssertionSuccess();
-}
-
-AssertionResult LowSpaceUserdata::ReadUserdataStats() {
-    struct statvfs buf;
-    if (statvfs(kUserDataDevice, &buf) == -1) {
-        return AssertionFailure() << strerror(errno);
-    }
-    bsize_ = buf.f_bsize;
-    free_space_ = bsize_ * buf.f_bfree;
-    available_space_ = bsize_ * buf.f_bavail;
-    return AssertionSuccess();
-}
-
-uint64_t LowSpaceUserdata::free_space() const {
-    CHECK(initialized_);
-    return free_space_;
-}
-
-uint64_t LowSpaceUserdata::available_space() const {
-    CHECK(initialized_);
-    return available_space_;
-}
-
-uint64_t LowSpaceUserdata::bsize() const {
-    CHECK(initialized_);
-    return bsize_;
-}
-
 bool IsVirtualAbEnabled() {
     return android::base::GetBoolProperty("ro.virtual_ab.enabled", false);
 }
diff --git a/init/builtins.cpp b/init/builtins.cpp
index bc23972..585eca2 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -60,6 +60,8 @@
 #include <cutils/android_reboot.h>
 #include <fs_mgr.h>
 #include <fscrypt/fscrypt.h>
+#include <libdm/dm.h>
+#include <libdm/loop_control.h>
 #include <libgsi/libgsi.h>
 #include <logwrap/logwrap.h>
 #include <private/android_filesystem_config.h>
@@ -506,29 +508,29 @@
 
     if (android::base::StartsWith(source, "loop@")) {
         int mode = (flags & MS_RDONLY) ? O_RDONLY : O_RDWR;
-        unique_fd fd(TEMP_FAILURE_RETRY(open(source + 5, mode | O_CLOEXEC)));
-        if (fd < 0) return ErrnoError() << "open(" << source + 5 << ", " << mode << ") failed";
+        const char* file_path = source + strlen("loop@");
 
-        for (size_t n = 0;; n++) {
-            std::string tmp = android::base::StringPrintf("/dev/block/loop%zu", n);
-            unique_fd loop(TEMP_FAILURE_RETRY(open(tmp.c_str(), mode | O_CLOEXEC)));
-            if (loop < 0) return ErrnoError() << "open(" << tmp << ", " << mode << ") failed";
-
-            loop_info info;
-            /* if it is a blank loop device */
-            if (ioctl(loop.get(), LOOP_GET_STATUS, &info) < 0 && errno == ENXIO) {
-                /* if it becomes our loop device */
-                if (ioctl(loop.get(), LOOP_SET_FD, fd.get()) >= 0) {
-                    if (mount(tmp.c_str(), target, system, flags, options) < 0) {
-                        ioctl(loop.get(), LOOP_CLR_FD, 0);
-                        return ErrnoError() << "mount() failed";
-                    }
-                    return {};
-                }
-            }
+        // Open source file
+        if (wait) {
+            wait_for_file(file_path, kCommandRetryTimeout);
         }
 
-        return Error() << "out of loopback devices";
+        unique_fd fd(TEMP_FAILURE_RETRY(open(file_path, mode | O_CLOEXEC)));
+        if (fd < 0) {
+            return ErrnoError() << "open(" << file_path << ", " << mode << ") failed";
+        }
+
+        // Allocate loop device and attach it to file_path.
+        android::dm::LoopControl loop_control;
+        std::string loop_device;
+        if (!loop_control.Attach(fd.get(), 5s, &loop_device)) {
+            return ErrnoError() << "loop_control.Attach " << file_path << " failed";
+        }
+
+        if (mount(loop_device.c_str(), target, system, flags, options) < 0) {
+            loop_control.Detach(loop_device);
+            return ErrnoError() << "mount() failed";
+        }
     } else {
         if (wait)
             wait_for_file(source, kCommandRetryTimeout);
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index 107e99a..bff80c5 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -58,6 +58,12 @@
 
 namespace {
 
+enum class BootMode {
+    NORMAL_MODE,
+    RECOVERY_MODE,
+    CHARGER_MODE,
+};
+
 void FreeRamdisk(DIR* dir, dev_t dev) {
     int dfd = dirfd(dir);
 
@@ -149,13 +155,27 @@
 }
 }  // namespace
 
-std::string GetModuleLoadList(bool recovery, const std::string& dir_path) {
-    auto module_load_file = "modules.load";
-    if (recovery) {
-        struct stat fileStat;
-        std::string recovery_load_path = dir_path + "/modules.load.recovery";
-        if (!stat(recovery_load_path.c_str(), &fileStat)) {
+std::string GetModuleLoadList(BootMode boot_mode, const std::string& dir_path) {
+    std::string module_load_file;
+
+    switch (boot_mode) {
+        case BootMode::NORMAL_MODE:
+            module_load_file = "modules.load";
+            break;
+        case BootMode::RECOVERY_MODE:
             module_load_file = "modules.load.recovery";
+            break;
+        case BootMode::CHARGER_MODE:
+            module_load_file = "modules.load.charger";
+            break;
+    }
+
+    if (module_load_file != "modules.load") {
+        struct stat fileStat;
+        std::string load_path = dir_path + "/" + module_load_file;
+        // Fall back to modules.load if the other files aren't accessible
+        if (stat(load_path.c_str(), &fileStat)) {
+            module_load_file = "modules.load";
         }
     }
 
@@ -163,7 +183,8 @@
 }
 
 #define MODULE_BASE_DIR "/lib/modules"
-bool LoadKernelModules(bool recovery, bool want_console, bool want_parallel, int& modules_loaded) {
+bool LoadKernelModules(BootMode boot_mode, bool want_console, bool want_parallel,
+                       int& modules_loaded) {
     struct utsname uts;
     if (uname(&uts)) {
         LOG(FATAL) << "Failed to get kernel version.";
@@ -203,7 +224,7 @@
     for (const auto& module_dir : module_dirs) {
         std::string dir_path = MODULE_BASE_DIR "/";
         dir_path.append(module_dir);
-        Modprobe m({dir_path}, GetModuleLoadList(recovery, dir_path));
+        Modprobe m({dir_path}, GetModuleLoadList(boot_mode, dir_path));
         bool retval = m.LoadListedModules(!want_console);
         modules_loaded = m.GetModuleCount();
         if (modules_loaded > 0) {
@@ -211,7 +232,7 @@
         }
     }
 
-    Modprobe m({MODULE_BASE_DIR}, GetModuleLoadList(recovery, MODULE_BASE_DIR));
+    Modprobe m({MODULE_BASE_DIR}, GetModuleLoadList(boot_mode, MODULE_BASE_DIR));
     bool retval = (want_parallel) ? m.LoadModulesParallel(std::thread::hardware_concurrency())
                                   : m.LoadListedModules(!want_console);
     modules_loaded = m.GetModuleCount();
@@ -221,6 +242,21 @@
     return true;
 }
 
+static bool IsChargerMode(const std::string& cmdline, const std::string& bootconfig) {
+    return bootconfig.find("androidboot.mode = \"charger\"") != std::string::npos ||
+            cmdline.find("androidboot.mode=charger") != std::string::npos;
+}
+
+static BootMode GetBootMode(const std::string& cmdline, const std::string& bootconfig)
+{
+    if (IsChargerMode(cmdline, bootconfig))
+        return BootMode::CHARGER_MODE;
+    else if (IsRecoveryMode() && !ForceNormalBoot(cmdline, bootconfig))
+        return BootMode::RECOVERY_MODE;
+
+    return BootMode::NORMAL_MODE;
+}
+
 int FirstStageMain(int argc, char** argv) {
     if (REBOOT_BOOTLOADER_ON_PANIC) {
         InstallRebootSignalHandlers();
@@ -328,7 +364,8 @@
 
     boot_clock::time_point module_start_time = boot_clock::now();
     int module_count = 0;
-    if (!LoadKernelModules(IsRecoveryMode() && !ForceNormalBoot(cmdline, bootconfig), want_console,
+    BootMode boot_mode = GetBootMode(cmdline, bootconfig);
+    if (!LoadKernelModules(boot_mode, want_console,
                            want_parallel, module_count)) {
         if (want_console != FirstStageConsoleParam::DISABLED) {
             LOG(ERROR) << "Failed to load kernel modules, starting console";
diff --git a/init/init.cpp b/init/init.cpp
index be1ebee..da63fdc 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -1043,6 +1043,12 @@
     SetProperty(gsi::kGsiBootedProp, is_running);
     auto is_installed = android::gsi::IsGsiInstalled() ? "1" : "0";
     SetProperty(gsi::kGsiInstalledProp, is_installed);
+    if (android::gsi::IsGsiRunning()) {
+        std::string dsu_slot;
+        if (android::gsi::GetActiveDsu(&dsu_slot)) {
+            SetProperty(gsi::kDsuSlotProp, dsu_slot);
+        }
+    }
 
     am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");
     am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");