Move ENOSPC tests to libfiemap.

These tests are still giving us trouble. Move them to libfiemap, which
(1) is closer to the source of implementation, and (2) allows us to
re-use the temporary filesystem code. This won't perturb the state of
the actual device.

The new test creates a 64MB ext4 or f2fs mount point as a sandbox, which
should be much safer.

Bug: 285197715
Bug: 298346574
Bug: 299142557
Test: fiemap_writer_test
Merged-In: I33502d49613be4f269a80e5c632514fc56a0246a
Ignore-AOSP-First: cherry-pick
Change-Id: Iedb7c32a594c3b1fca2904f3441029aaed7edf2a
diff --git a/fs_mgr/libfiemap/Android.bp b/fs_mgr/libfiemap/Android.bp
index 5deba65..ddda648 100644
--- a/fs_mgr/libfiemap/Android.bp
+++ b/fs_mgr/libfiemap/Android.bp
@@ -93,6 +93,9 @@
     test_options: {
         min_shipping_api_level: 29,
     },
+    header_libs: [
+        "libstorage_literals_headers",
+    ],
     require_root: true,
 }
 
diff --git a/fs_mgr/libfiemap/fiemap_writer_test.cpp b/fs_mgr/libfiemap/fiemap_writer_test.cpp
index c65481b..bd97a78 100644
--- a/fs_mgr/libfiemap/fiemap_writer_test.cpp
+++ b/fs_mgr/libfiemap/fiemap_writer_test.cpp
@@ -22,21 +22,25 @@
 #include <string.h>
 #include <sys/mount.h>
 #include <sys/stat.h>
+#include <sys/statvfs.h>
 #include <sys/types.h>
 #include <sys/vfs.h>
 #include <unistd.h>
 
 #include <string>
+#include <utility>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
 #include <android-base/unique_fd.h>
+#include <fstab/fstab.h>
 #include <gtest/gtest.h>
 #include <libdm/loop_control.h>
 #include <libfiemap/fiemap_writer.h>
 #include <libfiemap/split_fiemap_writer.h>
 #include <libgsi/libgsi.h>
+#include <storage_literals/storage_literals.h>
 
 #include "utility.h"
 
@@ -46,6 +50,7 @@
 using namespace std;
 using namespace std::string_literals;
 using namespace android::fiemap;
+using namespace android::storage_literals;
 using unique_fd = android::base::unique_fd;
 using LoopDevice = android::dm::LoopDevice;
 
@@ -427,90 +432,123 @@
     ASSERT_FALSE(ptr->Write(buffer.get(), kSize));
 }
 
-class VerifyBlockWritesExt4 : public ::testing::Test {
+// Get max file size and free space.
+std::pair<uint64_t, uint64_t> GetBigFileLimit(const std::string& mount_point) {
+    struct statvfs fs;
+    if (statvfs(mount_point.c_str(), &fs) < 0) {
+        PLOG(ERROR) << "statfs failed";
+        return {0, 0};
+    }
+
+    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};
+}
+
+class FsTest : public ::testing::Test {
+  protected:
     // 2GB Filesystem and 4k block size by default
     static constexpr uint64_t block_size = 4096;
-    static constexpr uint64_t fs_size = 2147483648;
+    static constexpr uint64_t fs_size = 64 * 1024 * 1024;
 
-  protected:
-    void SetUp() override {
-        fs_path = std::string(getenv("TMPDIR")) + "/ext4_2G.img";
+    void SetUp() {
+        android::fs_mgr::Fstab fstab;
+        ASSERT_TRUE(android::fs_mgr::ReadFstabFromFile("/proc/mounts", &fstab));
+
+        ASSERT_EQ(access(tmpdir_.path, F_OK), 0);
+        fs_path_ = tmpdir_.path + "/fs_image"s;
+        mntpoint_ = tmpdir_.path + "/mnt_point"s;
+
+        auto entry = android::fs_mgr::GetEntryForMountPoint(&fstab, "/data");
+        ASSERT_NE(entry, nullptr);
+        if (entry->fs_type == "ext4") {
+            SetUpExt4();
+        } else if (entry->fs_type == "f2fs") {
+            SetUpF2fs();
+        } else {
+            FAIL() << "Unrecognized fs_type: " << entry->fs_type;
+        }
+    }
+
+    void SetUpExt4() {
         uint64_t count = fs_size / block_size;
         std::string dd_cmd =
                 ::android::base::StringPrintf("/system/bin/dd if=/dev/zero of=%s bs=%" PRIu64
                                               " count=%" PRIu64 " > /dev/null 2>&1",
-                                              fs_path.c_str(), block_size, count);
+                                              fs_path_.c_str(), block_size, count);
         std::string mkfs_cmd =
-                ::android::base::StringPrintf("/system/bin/mkfs.ext4 -q %s", fs_path.c_str());
+                ::android::base::StringPrintf("/system/bin/mkfs.ext4 -q %s", fs_path_.c_str());
         // create mount point
-        mntpoint = std::string(getenv("TMPDIR")) + "/fiemap_mnt";
-        ASSERT_EQ(mkdir(mntpoint.c_str(), S_IRWXU), 0);
+        ASSERT_EQ(mkdir(mntpoint_.c_str(), S_IRWXU), 0);
         // create file for the file system
         int ret = system(dd_cmd.c_str());
         ASSERT_EQ(ret, 0);
         // Get and attach a loop device to the filesystem we created
-        LoopDevice loop_dev(fs_path, 10s);
+        LoopDevice loop_dev(fs_path_, 10s);
         ASSERT_TRUE(loop_dev.valid());
         // create file system
         ret = system(mkfs_cmd.c_str());
         ASSERT_EQ(ret, 0);
 
         // mount the file system
-        ASSERT_EQ(mount(loop_dev.device().c_str(), mntpoint.c_str(), "ext4", 0, nullptr), 0);
+        ASSERT_EQ(mount(loop_dev.device().c_str(), mntpoint_.c_str(), "ext4", 0, nullptr), 0);
     }
 
-    void TearDown() override {
-        umount(mntpoint.c_str());
-        rmdir(mntpoint.c_str());
-        unlink(fs_path.c_str());
-    }
-
-    std::string mntpoint;
-    std::string fs_path;
-};
-
-class VerifyBlockWritesF2fs : public ::testing::Test {
-    // 2GB Filesystem and 4k block size by default
-    static constexpr uint64_t block_size = 4096;
-    static constexpr uint64_t fs_size = 2147483648;
-
-  protected:
-    void SetUp() override {
-        fs_path = std::string(getenv("TMPDIR")) + "/f2fs_2G.img";
+    void SetUpF2fs() {
         uint64_t count = fs_size / block_size;
         std::string dd_cmd =
                 ::android::base::StringPrintf("/system/bin/dd if=/dev/zero of=%s bs=%" PRIu64
                                               " count=%" PRIu64 " > /dev/null 2>&1",
-                                              fs_path.c_str(), block_size, count);
+                                              fs_path_.c_str(), block_size, count);
         std::string mkfs_cmd =
-                ::android::base::StringPrintf("/system/bin/make_f2fs -q %s", fs_path.c_str());
+                ::android::base::StringPrintf("/system/bin/make_f2fs -q %s", fs_path_.c_str());
         // create mount point
-        mntpoint = std::string(getenv("TMPDIR")) + "/fiemap_mnt";
-        ASSERT_EQ(mkdir(mntpoint.c_str(), S_IRWXU), 0);
+        ASSERT_EQ(mkdir(mntpoint_.c_str(), S_IRWXU), 0);
         // create file for the file system
         int ret = system(dd_cmd.c_str());
         ASSERT_EQ(ret, 0);
         // Get and attach a loop device to the filesystem we created
-        LoopDevice loop_dev(fs_path, 10s);
+        LoopDevice loop_dev(fs_path_, 10s);
         ASSERT_TRUE(loop_dev.valid());
         // create file system
         ret = system(mkfs_cmd.c_str());
         ASSERT_EQ(ret, 0);
 
         // mount the file system
-        ASSERT_EQ(mount(loop_dev.device().c_str(), mntpoint.c_str(), "f2fs", 0, nullptr), 0);
+        ASSERT_EQ(mount(loop_dev.device().c_str(), mntpoint_.c_str(), "f2fs", 0, nullptr), 0);
     }
 
     void TearDown() override {
-        umount(mntpoint.c_str());
-        rmdir(mntpoint.c_str());
-        unlink(fs_path.c_str());
+        umount(mntpoint_.c_str());
+        rmdir(mntpoint_.c_str());
+        unlink(fs_path_.c_str());
     }
 
-    std::string mntpoint;
-    std::string fs_path;
+    TemporaryDir tmpdir_;
+    std::string mntpoint_;
+    std::string fs_path_;
 };
 
+TEST_F(FsTest, LowSpaceError) {
+    auto limits = GetBigFileLimit(mntpoint_);
+    ASSERT_GE(limits.first, 0);
+
+    FiemapUniquePtr ptr;
+
+    auto test_file = mntpoint_ + "/big_file";
+    auto status = FiemapWriter::Open(test_file, limits.first, &ptr);
+    ASSERT_FALSE(status.is_ok());
+    ASSERT_EQ(status.error_code(), FiemapStatus::ErrorCode::NO_SPACE);
+
+    // Also test for EFBIG.
+    status = FiemapWriter::Open(test_file, 16_TiB, &ptr);
+    ASSERT_FALSE(status.is_ok());
+    ASSERT_NE(status.error_code(), FiemapStatus::ErrorCode::NO_SPACE);
+}
+
 bool DetermineBlockSize() {
     struct statfs s;
     if (statfs(gTestDir.c_str(), &s)) {
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 460d49d..ce75a54 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -2312,32 +2312,6 @@
             << "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));
-
-    // 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.
-    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);
-
-    AddOperationForPartitions();
-
-    // Execute the update.
-    ASSERT_TRUE(sm->BeginUpdate());
-    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);
-}
-
 TEST_F(SnapshotUpdateTest, AddPartition) {
     group_->add_partition_names("dlkm");
 
@@ -2699,50 +2673,6 @@
                                     "Merge"s;
                          });
 
-class ImageManagerTest : public SnapshotTest {
-  protected:
-    void SetUp() override {
-        SKIP_IF_NON_VIRTUAL_AB();
-        SnapshotTest::SetUp();
-    }
-    void TearDown() override {
-        RETURN_IF_NON_VIRTUAL_AB();
-        CleanUp();
-    }
-    void CleanUp() {
-        if (!image_manager_) {
-            return;
-        }
-        EXPECT_TRUE(!image_manager_->BackingImageExists(kImageName) ||
-                    image_manager_->DeleteBackingImage(kImageName));
-    }
-
-    static constexpr const char* kImageName = "my_image";
-};
-
-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));
-
-        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";
-}
-
 bool Mkdir(const std::string& path) {
     if (mkdir(path.c_str(), 0700) && errno != EEXIST) {
         std::cerr << "Could not mkdir " << path << ": " << strerror(errno) << std::endl;
diff --git a/fs_mgr/libstorage_literals/storage_literals/storage_literals.h b/fs_mgr/libstorage_literals/storage_literals/storage_literals.h
index ac0dfbd..bbeabd5 100644
--- a/fs_mgr/libstorage_literals/storage_literals/storage_literals.h
+++ b/fs_mgr/libstorage_literals/storage_literals/storage_literals.h
@@ -37,6 +37,7 @@
 using KiB = Size<10>;
 using MiB = Size<20>;
 using GiB = Size<30>;
+using TiB = Size<40>;
 
 constexpr B operator""_B(unsigned long long v) {  // NOLINT
     return B{v};
@@ -54,6 +55,10 @@
     return GiB{v};
 }
 
+constexpr TiB operator""_TiB(unsigned long long v) {  // NOLINT
+    return TiB{v};
+}
+
 template <typename Dest, typename Src>
 constexpr Dest size_cast(Src src) {
     if (Src::power < Dest::power) {
@@ -69,6 +74,7 @@
 static_assert(1_KiB == 1 << 10);
 static_assert(1_MiB == 1 << 20);
 static_assert(1_GiB == 1 << 30);
+static_assert(1_TiB == 1ULL << 40);
 static_assert(size_cast<KiB>(1_B).count() == 0);
 static_assert(size_cast<KiB>(1024_B).count() == 1);
 static_assert(size_cast<KiB>(1_MiB).count() == 1024);