No public description

PiperOrigin-RevId: 604846219
diff --git a/centipede/BUILD b/centipede/BUILD
index 2aa9c97..153477c 100644
--- a/centipede/BUILD
+++ b/centipede/BUILD
@@ -616,6 +616,7 @@
         ":disable_riegeli": ["CENTIPEDE_DISABLE_RIEGELI"],
         "//conditions:default": [],
     }),
+    visibility = EXTENDED_API_VISIBILITY,
     deps = [
         ":defs",
         ":logging",
diff --git a/centipede/remote_file.cc b/centipede/remote_file.cc
index 48015a7..e2a7c68 100644
--- a/centipede/remote_file.cc
+++ b/centipede/remote_file.cc
@@ -139,6 +139,10 @@
   return std::filesystem::exists(path);
 }
 
+ABSL_ATTRIBUTE_WEAK bool RemotePathIsDirectory(std::string_view path) {
+  return std::filesystem::is_directory(path);
+}
+
 ABSL_ATTRIBUTE_WEAK int64_t RemoteFileGetSize(std::string_view path) {
   FILE *f = std::fopen(path.data(), "r");
   CHECK(f != nullptr) << VV(path);
@@ -171,6 +175,16 @@
   ::globfree(&glob_ret);
 }
 
+ABSL_ATTRIBUTE_WEAK std::vector<std::string> RemoteListDirectory(
+    std::string_view path) {
+  if (!std::filesystem::is_directory(path)) return {};
+  std::vector<std::string> ret;
+  for (const auto &entry : std::filesystem::directory_iterator(path)) {
+    ret.push_back(entry.path());
+  }
+  return ret;
+}
+
 ABSL_ATTRIBUTE_WEAK std::vector<std::string> RemoteListFilesRecursively(
     std::string_view path) {
   if (!std::filesystem::exists(path)) return {};
diff --git a/centipede/remote_file.h b/centipede/remote_file.h
index 19ca891..3d0ae64 100644
--- a/centipede/remote_file.h
+++ b/centipede/remote_file.h
@@ -76,12 +76,19 @@
 // Returns true if `path` exists.
 bool RemotePathExists(std::string_view path);
 
+// Returns true if `path` is a directory.
+bool RemotePathIsDirectory(std::string_view path);
+
 // Returns the size of the file at `path` in bytes. The file must exist.
 int64_t RemoteFileGetSize(std::string_view path);
 
 // Finds all files matching `glob` and appends them to `matches`.
 void RemoteGlobMatch(std::string_view glob, std::vector<std::string> &matches);
 
+// Returns a list of top-level paths under `path`. If `path` is not a directory,
+// or it's an empty directory, returns an empty list.
+std::vector<std::string> RemoteListDirectory(std::string_view path);
+
 // Recursively lists all files within `path`. Does not return any directories.
 // Returns an empty vector if `path` is an empty directory, or `path` does not
 // exist. Returns `{path}` if `path` is a non-directory.
diff --git a/fuzztest/BUILD b/fuzztest/BUILD
index e21c923..2871676 100644
--- a/fuzztest/BUILD
+++ b/fuzztest/BUILD
@@ -407,6 +407,7 @@
         "@com_google_absl//absl/hash",
         "@com_google_absl//absl/strings:str_format",
         "@com_google_absl//absl/strings:string_view",
+        "@com_google_fuzztest//centipede:remote_file",
     ],
 )
 
diff --git a/fuzztest/internal/io.cc b/fuzztest/internal/io.cc
index 3658908..eedd8df 100644
--- a/fuzztest/internal/io.cc
+++ b/fuzztest/internal/io.cc
@@ -14,11 +14,6 @@
 
 #include "./fuzztest/internal/io.h"
 
-#include <cerrno>
-#include <cstdio>
-#include <cstring>
-#include <filesystem>
-#include <fstream>
 #include <optional>
 #include <sstream>
 #include <string>
@@ -30,6 +25,7 @@
 #include "absl/hash/hash.h"
 #include "absl/strings/str_format.h"
 #include "absl/strings/string_view.h"
+#include "./centipede/remote_file.h"
 #include "./fuzztest/internal/logging.h"
 
 #if defined(__APPLE__)
@@ -39,55 +35,81 @@
      __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_13_0)
 // std::filesystem requires macOS 10.15+ or iOS 13+.
 // Just stub out these functions.
-#define STUB_FILESYSTEM
+#define FUZZTEST_STUB_FILESYSTEM
 #endif
 #endif
 
 namespace fuzztest::internal {
 
-#if defined(STUB_FILESYSTEM)
+#if defined(FUZZTEST_STUB_FILESYSTEM)
 
-bool WriteFile(absl::string_view filename, absl::string_view contents) {
-  FUZZTEST_INTERNAL_CHECK(false, "Can't replay in iOS/MacOS");
+bool WriteFile(absl::string_view path, absl::string_view contents) {
+  FUZZTEST_INTERNAL_CHECK(false, "Filesystem API not supported in iOS/MacOS");
 }
 
-std::string WriteDataToDir(absl::string_view data, absl::string_view dir) {
-  FUZZTEST_INTERNAL_CHECK(false, "Can't replay in iOS/MacOS");
+std::optional<std::string> ReadFile(absl::string_view path) {
+  FUZZTEST_INTERNAL_CHECK(false, "Filesystem API not supported in iOS/MacOS");
 }
 
-std::vector<FilePathAndData> ReadFileOrDirectory(
-    absl::string_view file_or_dir) {
-  FUZZTEST_INTERNAL_CHECK(false, "Can't replay in iOS/MacOS");
+bool IsDirectory(absl::string_view path) {
+  FUZZTEST_INTERNAL_CHECK(false, "Filesystem API not supported in iOS/MacOS");
 }
 
-std::optional<std::string> ReadFile(absl::string_view file) {
-  FUZZTEST_INTERNAL_CHECK(false, "Can't replay in iOS/MacOS");
+bool CreateDirectory(absl::string_view path) {
+  FUZZTEST_INTERNAL_CHECK(false, "Filesystem API not supported in iOS/MacOS");
 }
 
-std::vector<std::string> ListDirectory(absl::string_view dir) {
-  FUZZTEST_INTERNAL_CHECK(false, "Can't replay in iOS/MacOS");
+std::vector<std::string> ListDirectory(absl::string_view path) {
+  FUZZTEST_INTERNAL_CHECK(false, "Filesystem API not supported in iOS/MacOS");
 }
 
-#else  // defined(__APPLE__)
+std::vector<std::string> ListDirectoryRecursively(absl::string_view path) {
+  FUZZTEST_INTERNAL_CHECK(false, "Filesystem API not supported in iOS/MacOS");
+}
 
-bool WriteFile(absl::string_view filename, absl::string_view contents) {
-  std::filesystem::path file_path{
-      std::string_view{filename.data(), filename.size()}};
+#else  // FUZZTEST_STUB_FILESYSTEM
 
+bool WriteFile(absl::string_view path, absl::string_view contents) {
   // Just in case the directory does not currently exist.
-  // If it does, this is a noop.
-  std::filesystem::create_directories(file_path.parent_path());
-
-  std::ofstream file(file_path);
-  file << contents;
-  file.close();
-  if (!file.good()) {
-    absl::FPrintF(GetStderr(), "%s:%d: Error writing %s: (%d) %s\n", __FILE__,
-                  __LINE__, filename, errno, strerror(errno));
+  if (!CreateDirectory(Dirname(path))) {
+    absl::FPrintF(GetStderr(), "[!] %s:%d: Couldn't create directory: %s\n",
+                  __FILE__, __LINE__, path);
+    return false;
   }
-  return !file.fail();
+  centipede::RemoteFileSetContents(path, std::string(contents));
+  return true;
 }
 
+std::optional<std::string> ReadFile(absl::string_view path) {
+  std::string contents;
+  if (!centipede::RemotePathExists(path)) {
+    absl::FPrintF(GetStderr(), "[!] %s:%d: File doesn't exist: %s\n", __FILE__,
+                  __LINE__, path);
+    return std::nullopt;
+  }
+  centipede::RemoteFileGetContents(path, contents);
+  return contents;
+}
+
+bool IsDirectory(absl::string_view path) {
+  return centipede::RemotePathIsDirectory(path);
+}
+
+bool CreateDirectory(absl::string_view path) {
+  centipede::RemoteMkdir(path);
+  return true;
+}
+
+std::vector<std::string> ListDirectory(absl::string_view path) {
+  return centipede::RemoteListDirectory(path);
+}
+
+std::vector<std::string> ListDirectoryRecursively(absl::string_view path) {
+  return centipede::RemoteListFilesRecursively(path);
+}
+
+#endif  // FUZZTEST_STUB_FILESYSTEM
+
 std::string WriteDataToDir(absl::string_view data, absl::string_view outdir) {
   std::string filename(outdir);
   if (filename.back() != '/') filename += '/';
@@ -97,35 +119,21 @@
   return filename;
 }
 
-std::optional<std::string> ReadFile(absl::string_view file) {
-  std::filesystem::path file_path{std::string_view{file.data(), file.size()}};
-  if (!std::filesystem::is_regular_file(file_path)) return std::nullopt;
-  std::ifstream stream(file_path);
-  if (!stream.good()) {
-    absl::FPrintF(stderr, "%s:%d: Error reading %s: (%d) %s\n", __FILE__,
-                  __LINE__, file, errno, strerror(errno));
-    return std::nullopt;
-  }
-  std::stringstream buffer;
-  buffer << stream.rdbuf();
-  return buffer.str();
-}
-
 std::vector<FilePathAndData> ReadFileOrDirectory(
     absl::string_view file_or_dir) {
   std::vector<FilePathAndData> out;
+
   const auto try_append_file = [&](std::string path) {
-    std::optional<std::string> data = ReadFile(path);
-    if (data.has_value()) {
-      out.push_back(FilePathAndData{std::move(path), *std::move(data)});
+    std::optional<std::string> contents = ReadFile(path);
+    if (contents.has_value()) {
+      out.push_back(FilePathAndData{std::move(path), *std::move(contents)});
     }
   };
-  std::filesystem::path file_or_dir_path{
-      std::string_view{file_or_dir.data(), file_or_dir.size()}};
-  if (std::filesystem::is_directory(file_or_dir_path)) {
-    for (const auto& entry :
-         std::filesystem::recursive_directory_iterator(file_or_dir_path)) {
-      try_append_file(entry.path().string());
+  if (IsDirectory(file_or_dir)) {
+    for (const auto& path : ListDirectoryRecursively(file_or_dir)) {
+      if (!IsDirectory(path)) {
+        try_append_file(path);
+      }
     }
   } else {
     try_append_file(std::string(file_or_dir));
@@ -133,17 +141,13 @@
   return out;
 }
 
-std::vector<std::string> ListDirectory(absl::string_view dir) {
-  std::vector<std::string> out;
-  std::filesystem::path dir_path{std::string_view{dir.data(), dir.size()}};
-  if (!std::filesystem::is_directory(dir_path)) return out;
-  for (const auto& entry : std::filesystem::directory_iterator(dir_path)) {
-    out.push_back(entry.path().string());
-  }
-  return out;
-}
+absl::string_view Dirname(absl::string_view filename) {
+  auto last_slash_pos = filename.find_last_of("/\\");
 
-#endif  // defined(STUB_FILESYSTEM)
+  return last_slash_pos == absl::string_view::npos
+             ? filename
+             : filename.substr(0, last_slash_pos);
+}
 
 absl::string_view Basename(absl::string_view filename) {
   auto last_slash_pos = filename.find_last_of("/\\");
diff --git a/fuzztest/internal/io.h b/fuzztest/internal/io.h
index b2f4d9e..9ff7394 100644
--- a/fuzztest/internal/io.h
+++ b/fuzztest/internal/io.h
@@ -23,38 +23,53 @@
 
 namespace fuzztest::internal {
 
-bool WriteFile(absl::string_view filename, absl::string_view contents);
+// Writes `contents` to the file at `path`.  Returns true on success, false
+// otherwise.
+bool WriteFile(absl::string_view path, absl::string_view contents);
+
+// Returns the contents of the file at `path` or std::nullopt on failure.
+std::optional<std::string> ReadFile(absl::string_view path);
+
+// Returns true if `path` is a directory, false otherwise.
+bool IsDirectory(absl::string_view path);
+
+// Creates directory at `path`, *recursively* creating parent directories if
+// necessary. Returns true on success, false otherwise.
+bool CreateDirectory(absl::string_view path);
+
+// Returns a list of top-level paths under `path`. If `path` is not a directory,
+// or it's an empty directory, returns an empty list.
+std::vector<std::string> ListDirectory(absl::string_view path);
+
+// Returns all paths under `path` *recursively*. If `path` is not a directory,
+// returns an empty list.
+std::vector<std::string> ListDirectoryRecursively(absl::string_view path);
 
 // Write `data` to its hash-based filename in `dir`. Returns the `dir`-appended
 // path to the file.
 std::string WriteDataToDir(absl::string_view data, absl::string_view dir);
 
-// Reads `file` and returns its content. If `file` is not a regular file or
-// reading it fails, returns `std::nullopt`.
-std::optional<std::string> ReadFile(absl::string_view file);
-
 struct FilePathAndData {
   std::string path;
   std::string data;
 };
 
 // If `file_or_dir` is a directory, returns a list of its files' paths and
-// contents. If `file_or_dir` is a file, returns a singleton list with its path
-// and content. In all other cases, returns an empty list.
+// contents *recursively*. If `file_or_dir` is a file, returns a singleton list
+// with its path and content. In all other cases, returns an empty list.
 std::vector<FilePathAndData> ReadFileOrDirectory(absl::string_view file_or_dir);
 
-// Returns a list of top-level paths in `dir`. If `dir` is not a directory,
-// returns an empty list.
-std::vector<std::string> ListDirectory(absl::string_view dir);
-
-// Returns the basename of `filename`.
-absl::string_view Basename(absl::string_view filename);
-
 // Reads files as strings from the directory `dir` and returns a vector usable
 // by .WithSeeds().
 std::vector<std::tuple<std::string>> ReadFilesFromDirectory(
     absl::string_view dir);
 
+// Returns the dirname of `filename`.
+absl::string_view Dirname(absl::string_view filename);
+
+// Returns the basename of `filename`.
+absl::string_view Basename(absl::string_view filename);
+
 }  // namespace fuzztest::internal
 
 #endif  // FUZZTEST_FUZZTEST_INTERNAL_IO_H_