[f2fs] Add fs-test support for f2fs

Test: fx test basic-tests

Prerequisite: FUCHSIA_DIR$ patch -p1 < \
third_party/f2fs/patches/0002-f2fs-Enable-f2fs-to-be-tested-by-fs_test.patch

Build configuration: fx set core.x64 --with-base //bundles:tools \
-with-base //bundles:tests --with-base third_party/f2fs

Change-Id: I937e19fc78eb0632f11cb3a47bfc726fce4a71b7
Reviewed-on: https://fuchsia-review.googlesource.com/c/third_party/f2fs/+/536503
Reviewed-by: Brett Wilson <brettw@google.com>
diff --git a/patches/0002-f2fs-Enable-f2fs-to-be-tested-by-fs_test.patch b/patches/0002-f2fs-Enable-f2fs-to-be-tested-by-fs_test.patch
new file mode 100644
index 0000000..99ae607
--- /dev/null
+++ b/patches/0002-f2fs-Enable-f2fs-to-be-tested-by-fs_test.patch
@@ -0,0 +1,192 @@
+From e93b40346183b48df82d4a1a89ffe3a25a397a47 Mon Sep 17 00:00:00 2001
+From: Dongjin Kim <dongjin_.kim@samsung.com>
+Date: Mon, 24 May 2021 15:38:02 +0900
+Subject: [PATCH] [f2fs] Enable f2fs to be tested by fs_test
+
+Add f2fs dependency for fs_management and fs_test
+Add f2fs options for fs_test
+
+Change-Id: I7f311852576223da06293977c639771d5154cf40
+---
+ src/lib/storage/fs_management/cpp/BUILD.gn |  1 +
+ src/storage/fs_test/BUILD.gn               |  2 +
+ src/storage/fs_test/fs_test.cc             | 69 +++++++++++++++++++++-
+ src/storage/fs_test/fs_test.h              | 29 +++++++++
+ 4 files changed, 100 insertions(+), 1 deletion(-)
+
+diff --git a/src/lib/storage/fs_management/cpp/BUILD.gn b/src/lib/storage/fs_management/cpp/BUILD.gn
+index 62845d92d5a..5d9a915b25b 100644
+--- a/src/lib/storage/fs_management/cpp/BUILD.gn
++++ b/src/lib/storage/fs_management/cpp/BUILD.gn
+@@ -102,6 +102,7 @@ test("fs-management-test") {
+     "//src/storage/bin/blobfs",
+     "//src/storage/bin/minfs",
+     "//src/storage/bin/minfs",
++    "//third_party/f2fs/tools:f2fs",
+     "//zircon/third_party/uapp/fsck-msdosfs",
+     "//zircon/third_party/uapp/mkfs-msdosfs",
+   ]
+diff --git a/src/storage/fs_test/BUILD.gn b/src/storage/fs_test/BUILD.gn
+index 371e7beed17..6d0bf087145 100644
+--- a/src/storage/fs_test/BUILD.gn
++++ b/src/storage/fs_test/BUILD.gn
+@@ -587,6 +587,7 @@ fuchsia_test_package("fs-tests") {
+   deps = [
+     "//src/storage/bin/blobfs",
+     "//src/storage/bin/minfs",
++    "//third_party/f2fs/tools:f2fs",
+     "//zircon/third_party/uapp/fsck-msdosfs",
+     "//zircon/third_party/uapp/mkfs-msdosfs",
+   ]
+@@ -660,6 +661,7 @@ fuchsia_test_package("large-fs-tests") {
+   ]
+   deps = [
+     "//src/storage/bin/minfs",
++    "//third_party/f2fs/tools:f2fs",
+     "//zircon/third_party/uapp/fsck-msdosfs",
+     "//zircon/third_party/uapp/mkfs-msdosfs",
+   ]
+diff --git a/src/storage/fs_test/fs_test.cc b/src/storage/fs_test/fs_test.cc
+index 217cc152638..8352002c309 100644
+--- a/src/storage/fs_test/fs_test.cc
++++ b/src/storage/fs_test/fs_test.cc
+@@ -408,6 +408,14 @@ TestFilesystemOptions TestFilesystemOptions::BlobfsWithoutFvm() {
+   return blobfs_with_no_fvm;
+ }
+ 
++TestFilesystemOptions TestFilesystemOptions::F2fsWithoutFvm() {
++  return TestFilesystemOptions{.description = "F2fs",
++                               .use_fvm = false,
++                               .device_block_size = 512,
++                               .device_block_count = 2'097'152,
++                               .filesystem = &F2fsFilesystem::SharedInstance()};
++}
++
+ std::ostream& operator<<(std::ostream& out, const TestFilesystemOptions& options) {
+   return out << options.description;
+ }
+@@ -424,7 +432,11 @@ std::vector<TestFilesystemOptions> AllTestFilesystems() {
+     TestFilesystemOptions::DefaultMinfs(), TestFilesystemOptions::MinfsWithoutFvm(),
+         TestFilesystemOptions::DefaultMemfs(), TestFilesystemOptions::DefaultFatfs(),
+ #if 0  // Change to 1 to enable testing for Fxfs
+-      DefaultFxfsTestOptions()
++      DefaultFxfsTestOptions(),
++#endif
++// TODO(unknown): set default value to 0
++#if 1  // Change to 1 to enable testing for F2fs
++        TestFilesystemOptions::F2fsWithoutFvm()
+ #endif
+   };
+ }
+@@ -689,6 +701,61 @@ zx::status<std::unique_ptr<FilesystemInstance>> BlobfsFilesystem::Open(
+   return zx::ok(std::make_unique<BlobfsInstance>(std::move(ram_nand), std::move(device_path)));
+ }
+ 
++// -- F2fs --
++
++class F2fsInstance : public FilesystemInstance {
++ public:
++  F2fsInstance(RamDevice device, std::string device_path)
++      : device_(std::move(device)), device_path_(std::move(device_path)) {}
++
++  virtual zx::status<> Format(const TestFilesystemOptions& options) override {
++    return FsFormat(device_path_, DISK_FORMAT_F2FS, default_mkfs_options);
++  }
++
++  zx::status<> Mount(const std::string& mount_path, const mount_options_t& options) override {
++    return FsMount(device_path_, mount_path, DISK_FORMAT_F2FS, options);
++  }
++
++  zx::status<> Fsck() override {
++    // TODO(unknown): Set appropriate options for F2fs
++    fsck_options_t options{
++        .verbose = false,
++        .never_modify = true,
++        .always_modify = false,
++        .force = true,
++    };
++    return zx::make_status(
++        fsck(device_path_.c_str(), DISK_FORMAT_F2FS, &options, launch_stdio_sync));
++  }
++
++  zx::status<std::string> DevicePath() const override { return zx::ok(std::string(device_path_)); }
++
++  storage::RamDisk* GetRamDisk() override { return std::get_if<storage::RamDisk>(&device_); }
++
++  ramdevice_client::RamNand* GetRamNand() override {
++    return std::get_if<ramdevice_client::RamNand>(&device_);
++  }
++
++ private:
++  RamDevice device_;
++  std::string device_path_;
++};
++
++std::unique_ptr<FilesystemInstance> F2fsFilesystem::Create(RamDevice device,
++                                                           std::string device_path) const {
++  return std::make_unique<F2fsInstance>(std::move(device), std::move(device_path));
++}
++
++zx::status<std::unique_ptr<FilesystemInstance>> F2fsFilesystem::Open(
++    const TestFilesystemOptions& options) const {
++  auto result = OpenRamNand(options);
++  if (result.is_error()) {
++    return result.take_error();
++  }
++  auto [ram_nand, device_path] = std::move(result).value();
++  return zx::ok(std::make_unique<F2fsInstance>(std::move(ram_nand), std::move(device_path)));
++}
++
+ // --
+ 
+ zx::status<TestFilesystem> TestFilesystem::FromInstance(
+diff --git a/src/storage/fs_test/fs_test.h b/src/storage/fs_test/fs_test.h
+index e819f7f2537..90e7c5bd03c 100644
+--- a/src/storage/fs_test/fs_test.h
++++ b/src/storage/fs_test/fs_test.h
+@@ -43,6 +43,7 @@ struct TestFilesystemOptions {
+   static TestFilesystemOptions DefaultFatfs();
+   static TestFilesystemOptions DefaultBlobfs();
+   static TestFilesystemOptions BlobfsWithoutFvm();
++  static TestFilesystemOptions F2fsWithoutFvm();
+ 
+   std::string description;
+   bool use_ram_nand = false;
+@@ -210,6 +211,34 @@ class FatFilesystem : public FilesystemImpl<FatFilesystem> {
+   }
+ };
+ 
++// Support for F2fs.
++class F2fsFilesystem : public FilesystemImplWithDefaultMake<F2fsFilesystem> {
++ public:
++  const Traits& GetTraits() const override {
++    static Traits traits{
++        .name = "f2fs",
++        .can_unmount = true,
++        .timestamp_granularity = zx::nsec(1),
++        .supports_hard_links = false,
++        .supports_mmap = false,
++        .supports_resize = false,
++        // TODO(unknown): Get value from f2fs header
++        .max_file_size = 4'329'690'886'144,
++        .in_memory = false,
++        .is_case_sensitive = true,
++        .supports_sparse_files = true,
++        .supports_fsck_after_every_transaction = false,
++    };
++    return traits;
++  }
++
++  std::unique_ptr<FilesystemInstance> Create(RamDevice device,
++                                             std::string device_path) const override;
++
++  zx::status<std::unique_ptr<FilesystemInstance>> Open(
++      const TestFilesystemOptions& options) const override;
++};
++
+ // Helper that creates a test file system with the given options and will clean-up upon destruction.
+ class TestFilesystem {
+  public:
+-- 
+2.25.1
+