Make FileSystem a Customizable Class (#8649)

Summary: Pull Request resolved: https://github.com/facebook/rocksdb/pull/8649

Reviewed By: zhichao-cao

Differential Revision: D32036059

Pulled By: mrambacher

fbshipit-source-id: 4f1e7557ecac52eb849b83ae02b8d7d232112295
main
mrambacher 3 years ago committed by Facebook GitHub Bot
parent cfc57f55b5
commit f72c834eab
  1. 3
      HISTORY.md
  2. 3
      db/db_basic_test.cc
  3. 3
      env/env.cc
  4. 65
      env/env_chroot.cc
  5. 32
      env/env_chroot.h
  6. 32
      env/env_encryption.cc
  7. 6
      env/env_encryption_ctr.h
  8. 167
      env/env_test.cc
  9. 112
      env/file_system.cc
  10. 3
      env/file_system_tracer.h
  11. 14
      env/fs_posix.cc
  12. 3
      env/fs_readonly.h
  13. 8
      env/fs_remap.h
  14. 107
      env/mock_env.cc
  15. 106
      env/mock_env.h
  16. 3
      file/prefetch_test.cc
  17. 11
      include/rocksdb/env_encryption.h
  18. 20
      include/rocksdb/file_system.h
  19. 37
      options/customizable_test.cc
  20. 5
      port/win/env_win.h
  21. 3
      test_util/testutil.h
  22. 130
      utilities/env_timed.cc
  23. 97
      utilities/env_timed.h
  24. 3
      utilities/fault_injection_fs.h

@ -15,6 +15,9 @@
### Public Interface Change ### Public Interface Change
* When options.ttl is used with leveled compaction with compactinon priority kMinOverlappingRatio, files exceeding half of TTL value will be prioritized more, so that by the time TTL is reached, fewer extra compactions will be scheduled to clear them up. At the same time, when compacting files with data older than half of TTL, output files may be cut off based on those files' boundaries, in order for the early TTL compaction to work properly. * When options.ttl is used with leveled compaction with compactinon priority kMinOverlappingRatio, files exceeding half of TTL value will be prioritized more, so that by the time TTL is reached, fewer extra compactions will be scheduled to clear them up. At the same time, when compacting files with data older than half of TTL, output files may be cut off based on those files' boundaries, in order for the early TTL compaction to work properly.
### Public API change
* Made FileSystem extend the Customizable class and added a CreateFromString method. Implementations need to be registered with the ObjectRegistry and to implement a Name() method in order to be created via this method.
## 6.26.0 (2021-10-20) ## 6.26.0 (2021-10-20)
### Bug Fixes ### Bug Fixes
* Fixes a bug in directed IO mode when calling MultiGet() for blobs in the same blob file. The bug is caused by not sorting the blob read requests by file offsets. * Fixes a bug in directed IO mode when calling MultiGet() for blobs in the same blob file. The bug is caused by not sorting the blob read requests by file offsets.

@ -3296,6 +3296,9 @@ class DeadlineFS : public FileSystemWrapper {
ignore_deadline_(false), ignore_deadline_(false),
error_on_delay_(error_on_delay) {} error_on_delay_(error_on_delay) {}
static const char* kClassName() { return "DeadlineFileSystem"; }
const char* Name() const override { return kClassName(); }
IOStatus NewRandomAccessFile(const std::string& fname, IOStatus NewRandomAccessFile(const std::string& fname,
const FileOptions& opts, const FileOptions& opts,
std::unique_ptr<FSRandomAccessFile>* result, std::unique_ptr<FSRandomAccessFile>* result,

3
env/env.cc vendored

@ -324,7 +324,8 @@ class LegacyFileSystemWrapper : public FileSystem {
explicit LegacyFileSystemWrapper(Env* t) : target_(t) {} explicit LegacyFileSystemWrapper(Env* t) : target_(t) {}
~LegacyFileSystemWrapper() override {} ~LegacyFileSystemWrapper() override {}
const char* Name() const override { return "Legacy File System"; } static const char* kClassName() { return "LegacyFileSystem"; }
const char* Name() const override { return kClassName(); }
// Return the target to which this Env forwards all calls // Return the target to which this Env forwards all calls
Env* target() const { return target_; } Env* target() const { return target_; }

65
env/env_chroot.cc vendored

@ -13,20 +13,35 @@
#include "env/composite_env_wrapper.h" #include "env/composite_env_wrapper.h"
#include "env/fs_remap.h" #include "env/fs_remap.h"
#include "rocksdb/utilities/options_type.h"
#include "util/string_util.h" // errnoStr #include "util/string_util.h" // errnoStr
namespace ROCKSDB_NAMESPACE { namespace ROCKSDB_NAMESPACE {
namespace { namespace {
class ChrootFileSystem : public RemapFileSystem { static std::unordered_map<std::string, OptionTypeInfo> chroot_fs_type_info = {
public: {"chroot_dir", {0, OptionType::kString}}};
ChrootFileSystem(const std::shared_ptr<FileSystem>& base, } // namespace
ChrootFileSystem::ChrootFileSystem(const std::shared_ptr<FileSystem>& base,
const std::string& chroot_dir) const std::string& chroot_dir)
: RemapFileSystem(base) { : RemapFileSystem(base), chroot_dir_(chroot_dir) {
RegisterOptions("chroot_dir", &chroot_dir_, &chroot_fs_type_info);
}
Status ChrootFileSystem::PrepareOptions(const ConfigOptions& options) {
Status s = FileSystemWrapper::PrepareOptions(options);
if (!s.ok()) {
return s;
} else if (chroot_dir_.empty()) {
s = Status::InvalidArgument("ChRootFileSystem requires a chroot dir");
} else {
s = target_->FileExists(chroot_dir_, IOOptions(), nullptr);
}
if (s.ok()) {
#if defined(OS_AIX) #if defined(OS_AIX)
char resolvedName[PATH_MAX]; char resolvedName[PATH_MAX];
char* real_chroot_dir = realpath(chroot_dir.c_str(), resolvedName); char* real_chroot_dir = realpath(chroot_dir_.c_str(), resolvedName);
#else #else
char* real_chroot_dir = realpath(chroot_dir.c_str(), nullptr); char* real_chroot_dir = realpath(chroot_dir_.c_str(), nullptr);
#endif #endif
// chroot_dir must exist so realpath() returns non-nullptr. // chroot_dir must exist so realpath() returns non-nullptr.
assert(real_chroot_dir != nullptr); assert(real_chroot_dir != nullptr);
@ -35,11 +50,12 @@ class ChrootFileSystem : public RemapFileSystem {
free(real_chroot_dir); free(real_chroot_dir);
#endif #endif
} }
return s;
}
const char* Name() const override { return "ChrootFS"; } IOStatus ChrootFileSystem::GetTestDirectory(const IOOptions& options,
std::string* path,
IOStatus GetTestDirectory(const IOOptions& options, std::string* path, IODebugContext* dbg) {
IODebugContext* dbg) override {
// Adapted from PosixEnv's implementation since it doesn't provide a way to // Adapted from PosixEnv's implementation since it doesn't provide a way to
// create directory in the chroot. // create directory in the chroot.
char buf[256]; char buf[256];
@ -50,12 +66,11 @@ class ChrootFileSystem : public RemapFileSystem {
return CreateDirIfMissing(*path, options, dbg); return CreateDirIfMissing(*path, options, dbg);
} }
protected:
// Returns status and expanded absolute path including the chroot directory. // Returns status and expanded absolute path including the chroot directory.
// Checks whether the provided path breaks out of the chroot. If it returns // Checks whether the provided path breaks out of the chroot. If it returns
// non-OK status, the returned path should not be used. // non-OK status, the returned path should not be used.
std::pair<IOStatus, std::string> EncodePath( std::pair<IOStatus, std::string> ChrootFileSystem::EncodePath(
const std::string& path) override { const std::string& path) {
if (path.empty() || path[0] != '/') { if (path.empty() || path[0] != '/') {
return {IOStatus::InvalidArgument(path, "Not an absolute path"), ""}; return {IOStatus::InvalidArgument(path, "Not an absolute path"), ""};
} }
@ -85,8 +100,8 @@ class ChrootFileSystem : public RemapFileSystem {
// Similar to EncodePath() except assumes the basename in the path hasn't been // Similar to EncodePath() except assumes the basename in the path hasn't been
// created yet. // created yet.
std::pair<IOStatus, std::string> EncodePathWithNewBasename( std::pair<IOStatus, std::string> ChrootFileSystem::EncodePathWithNewBasename(
const std::string& path) override { const std::string& path) {
if (path.empty() || path[0] != '/') { if (path.empty() || path[0] != '/') {
return {IOStatus::InvalidArgument(path, "Not an absolute path"), ""}; return {IOStatus::InvalidArgument(path, "Not an absolute path"), ""};
} }
@ -105,23 +120,27 @@ class ChrootFileSystem : public RemapFileSystem {
return status_and_enc_path; return status_and_enc_path;
} }
private:
std::string chroot_dir_;
};
} // namespace
std::shared_ptr<FileSystem> NewChrootFileSystem( std::shared_ptr<FileSystem> NewChrootFileSystem(
const std::shared_ptr<FileSystem>& base, const std::string& chroot_dir) { const std::shared_ptr<FileSystem>& base, const std::string& chroot_dir) {
return std::make_shared<ChrootFileSystem>(base, chroot_dir); auto chroot_fs = std::make_shared<ChrootFileSystem>(base, chroot_dir);
Status s = chroot_fs->PrepareOptions(ConfigOptions());
if (s.ok()) {
return chroot_fs;
} else {
return nullptr;
}
} }
Env* NewChrootEnv(Env* base_env, const std::string& chroot_dir) { Env* NewChrootEnv(Env* base_env, const std::string& chroot_dir) {
if (!base_env->FileExists(chroot_dir).ok()) { if (!base_env->FileExists(chroot_dir).ok()) {
return nullptr; return nullptr;
} }
std::shared_ptr<FileSystem> chroot_fs = auto chroot_fs = NewChrootFileSystem(base_env->GetFileSystem(), chroot_dir);
NewChrootFileSystem(base_env->GetFileSystem(), chroot_dir); if (chroot_fs != nullptr) {
return new CompositeEnvWrapper(base_env, chroot_fs); return new CompositeEnvWrapper(base_env, chroot_fs);
} else {
return nullptr;
}
} }
} // namespace ROCKSDB_NAMESPACE } // namespace ROCKSDB_NAMESPACE

32
env/env_chroot.h vendored

@ -9,9 +9,37 @@
#include <string> #include <string>
#include "rocksdb/env.h" #include "env/fs_remap.h"
#include "rocksdb/file_system.h"
namespace ROCKSDB_NAMESPACE { namespace ROCKSDB_NAMESPACE {
class ChrootFileSystem : public RemapFileSystem {
public:
ChrootFileSystem(const std::shared_ptr<FileSystem>& base,
const std::string& chroot_dir);
static const char* kClassName() { return "ChrootFS"; }
const char* Name() const override { return kClassName(); }
IOStatus GetTestDirectory(const IOOptions& options, std::string* path,
IODebugContext* dbg) override;
Status PrepareOptions(const ConfigOptions& options) override;
protected:
// Returns status and expanded absolute path including the chroot directory.
// Checks whether the provided path breaks out of the chroot. If it returns
// non-OK status, the returned path should not be used.
std::pair<IOStatus, std::string> EncodePath(const std::string& path) override;
// Similar to EncodePath() except assumes the basename in the path hasn't been
// created yet.
std::pair<IOStatus, std::string> EncodePathWithNewBasename(
const std::string& path) override;
private:
std::string chroot_dir_;
};
// Returns an Env that translates paths such that the root directory appears to // Returns an Env that translates paths such that the root directory appears to
// be chroot_dir. chroot_dir should refer to an existing directory. // be chroot_dir. chroot_dir should refer to an existing directory.
@ -19,6 +47,8 @@ namespace ROCKSDB_NAMESPACE {
// This class has not been fully analyzed for providing strong security // This class has not been fully analyzed for providing strong security
// guarantees. // guarantees.
Env* NewChrootEnv(Env* base_env, const std::string& chroot_dir); Env* NewChrootEnv(Env* base_env, const std::string& chroot_dir);
std::shared_ptr<FileSystem> NewChrootFileSystem(
const std::shared_ptr<FileSystem>& base, const std::string& chroot_dir);
} // namespace ROCKSDB_NAMESPACE } // namespace ROCKSDB_NAMESPACE

@ -438,11 +438,20 @@ IOStatus EncryptedRandomRWFile::Close(const IOOptions& options,
} }
namespace { namespace {
static std::unordered_map<std::string, OptionTypeInfo> encrypted_fs_type_info =
{
{"provider",
OptionTypeInfo::AsCustomSharedPtr<EncryptionProvider>(
0 /* No offset, whole struct*/, OptionVerificationType::kByName,
OptionTypeFlags::kNone)},
};
// EncryptedFileSystemImpl implements an FileSystemWrapper that adds encryption // EncryptedFileSystemImpl implements an FileSystemWrapper that adds encryption
// to files stored on disk. // to files stored on disk.
class EncryptedFileSystemImpl : public EncryptedFileSystem { class EncryptedFileSystemImpl : public EncryptedFileSystem {
public: public:
const char* Name() const override { return "EncryptedFS"; } const char* Name() const override {
return EncryptedFileSystem::kClassName();
}
// Returns the raw encryption provider that should be used to write the input // Returns the raw encryption provider that should be used to write the input
// encrypted file. If there is no such provider, NotFound is returned. // encrypted file. If there is no such provider, NotFound is returned.
IOStatus GetWritableProvider(const std::string& /*fname*/, IOStatus GetWritableProvider(const std::string& /*fname*/,
@ -664,6 +673,7 @@ class EncryptedFileSystemImpl : public EncryptedFileSystem {
const std::shared_ptr<EncryptionProvider>& provider) const std::shared_ptr<EncryptionProvider>& provider)
: EncryptedFileSystem(base) { : EncryptedFileSystem(base) {
provider_ = provider; provider_ = provider;
RegisterOptions("EncryptionProvider", &provider_, &encrypted_fs_type_info);
} }
Status AddCipher(const std::string& descriptor, const char* cipher, Status AddCipher(const std::string& descriptor, const char* cipher,
@ -910,10 +920,28 @@ class EncryptedFileSystemImpl : public EncryptedFileSystem {
}; };
} // namespace } // namespace
Status NewEncryptedFileSystemImpl(
const std::shared_ptr<FileSystem>& base,
const std::shared_ptr<EncryptionProvider>& provider,
std::unique_ptr<FileSystem>* result) {
result->reset(new EncryptedFileSystemImpl(base, provider));
return Status::OK();
}
std::shared_ptr<FileSystem> NewEncryptedFS( std::shared_ptr<FileSystem> NewEncryptedFS(
const std::shared_ptr<FileSystem>& base, const std::shared_ptr<FileSystem>& base,
const std::shared_ptr<EncryptionProvider>& provider) { const std::shared_ptr<EncryptionProvider>& provider) {
return std::make_shared<EncryptedFileSystemImpl>(base, provider); std::unique_ptr<FileSystem> efs;
Status s = NewEncryptedFileSystemImpl(base, provider, &efs);
if (s.ok()) {
s = efs->PrepareOptions(ConfigOptions());
}
if (s.ok()) {
std::shared_ptr<FileSystem> result(efs.release());
return result;
} else {
return nullptr;
}
} }
// Returns an Env that encrypts data when stored on disk and decrypts data when // Returns an Env that encrypts data when stored on disk and decrypts data when
// read from disk. // read from disk.

@ -105,6 +105,12 @@ class CTREncryptionProvider : public EncryptionProvider {
uint64_t initialCounter, const Slice& iv, const Slice& prefix, uint64_t initialCounter, const Slice& iv, const Slice& prefix,
std::unique_ptr<BlockAccessCipherStream>* result); std::unique_ptr<BlockAccessCipherStream>* result);
}; };
Status NewEncryptedFileSystemImpl(
const std::shared_ptr<FileSystem>& base_fs,
const std::shared_ptr<EncryptionProvider>& provider,
std::unique_ptr<FileSystem>* fs);
} // namespace ROCKSDB_NAMESPACE } // namespace ROCKSDB_NAMESPACE
#endif // !defined(ROCKSDB_LITE) #endif // !defined(ROCKSDB_LITE)

167
env/env_test.cc vendored

@ -39,6 +39,7 @@
#include "env/emulated_clock.h" #include "env/emulated_clock.h"
#include "env/env_chroot.h" #include "env/env_chroot.h"
#include "env/env_encryption_ctr.h" #include "env/env_encryption_ctr.h"
#include "env/fs_readonly.h"
#include "env/unique_id_gen.h" #include "env/unique_id_gen.h"
#include "logging/log_buffer.h" #include "logging/log_buffer.h"
#include "logging/logging.h" #include "logging/logging.h"
@ -59,6 +60,7 @@
#include "util/mutexlock.h" #include "util/mutexlock.h"
#include "util/random.h" #include "util/random.h"
#include "util/string_util.h" #include "util/string_util.h"
#include "utilities/env_timed.h"
#include "utilities/fault_injection_env.h" #include "utilities/fault_injection_env.h"
#include "utilities/fault_injection_fs.h" #include "utilities/fault_injection_fs.h"
@ -2233,14 +2235,17 @@ INSTANTIATE_TEST_CASE_P(DefaultEnvWithDirectIO, EnvPosixTestWithParam,
#endif // !defined(ROCKSDB_LITE) #endif // !defined(ROCKSDB_LITE)
#if !defined(ROCKSDB_LITE) && !defined(OS_WIN) #if !defined(ROCKSDB_LITE) && !defined(OS_WIN)
static Env* GetChrootEnv() {
static std::unique_ptr<Env> chroot_env( static std::unique_ptr<Env> chroot_env(
NewChrootEnv(Env::Default(), test::TmpDir(Env::Default()))); NewChrootEnv(Env::Default(), test::TmpDir(Env::Default())));
INSTANTIATE_TEST_CASE_P( return chroot_env.get();
ChrootEnvWithoutDirectIO, EnvPosixTestWithParam, }
::testing::Values(std::pair<Env*, bool>(chroot_env.get(), false))); INSTANTIATE_TEST_CASE_P(ChrootEnvWithoutDirectIO, EnvPosixTestWithParam,
INSTANTIATE_TEST_CASE_P( ::testing::Values(std::pair<Env*, bool>(GetChrootEnv(),
ChrootEnvWithDirectIO, EnvPosixTestWithParam, false)));
::testing::Values(std::pair<Env*, bool>(chroot_env.get(), true))); INSTANTIATE_TEST_CASE_P(ChrootEnvWithDirectIO, EnvPosixTestWithParam,
::testing::Values(std::pair<Env*, bool>(GetChrootEnv(),
true)));
#endif // !defined(ROCKSDB_LITE) && !defined(OS_WIN) #endif // !defined(ROCKSDB_LITE) && !defined(OS_WIN)
class EnvFSTestWithParam class EnvFSTestWithParam
@ -2492,7 +2497,6 @@ TEST_F(CreateEnvTest, CreateMockSystemClock) {
guard->reset(new MockSystemClock(nullptr)); guard->reset(new MockSystemClock(nullptr));
return guard->get(); return guard->get();
}); });
ASSERT_OK(SystemClock::CreateFromString( ASSERT_OK(SystemClock::CreateFromString(
config_options_, EmulatedSystemClock::kClassName(), &mock)); config_options_, EmulatedSystemClock::kClassName(), &mock));
ASSERT_NE(mock, nullptr); ASSERT_NE(mock, nullptr);
@ -2518,6 +2522,154 @@ TEST_F(CreateEnvTest, CreateMockSystemClock) {
ASSERT_OK(SystemClock::CreateFromString( ASSERT_OK(SystemClock::CreateFromString(
config_options_, EmulatedSystemClock::kClassName(), &mock)); config_options_, EmulatedSystemClock::kClassName(), &mock));
} }
TEST_F(CreateEnvTest, CreateReadOnlyFileSystem) {
std::shared_ptr<FileSystem> fs, copy;
ASSERT_OK(FileSystem::CreateFromString(
config_options_, ReadOnlyFileSystem::kClassName(), &fs));
ASSERT_NE(fs, nullptr);
ASSERT_STREQ(fs->Name(), ReadOnlyFileSystem::kClassName());
ASSERT_EQ(fs->Inner(), FileSystem::Default().get());
std::string opts_str = fs->ToString(config_options_);
std::string mismatch;
ASSERT_OK(FileSystem::CreateFromString(config_options_, opts_str, &copy));
ASSERT_TRUE(fs->AreEquivalent(config_options_, copy.get(), &mismatch));
ASSERT_OK(FileSystem::CreateFromString(
config_options_,
std::string("id=") + ReadOnlyFileSystem::kClassName() +
"; target=" + TimedFileSystem::kClassName(),
&fs));
ASSERT_NE(fs, nullptr);
opts_str = fs->ToString(config_options_);
ASSERT_STREQ(fs->Name(), ReadOnlyFileSystem::kClassName());
ASSERT_NE(fs->Inner(), nullptr);
ASSERT_STREQ(fs->Inner()->Name(), TimedFileSystem::kClassName());
ASSERT_EQ(fs->Inner()->Inner(), FileSystem::Default().get());
ASSERT_OK(FileSystem::CreateFromString(config_options_, opts_str, &copy));
ASSERT_TRUE(fs->AreEquivalent(config_options_, copy.get(), &mismatch));
}
TEST_F(CreateEnvTest, CreateTimedFileSystem) {
std::shared_ptr<FileSystem> fs, copy;
ASSERT_OK(FileSystem::CreateFromString(config_options_,
TimedFileSystem::kClassName(), &fs));
ASSERT_NE(fs, nullptr);
ASSERT_STREQ(fs->Name(), TimedFileSystem::kClassName());
ASSERT_EQ(fs->Inner(), FileSystem::Default().get());
std::string opts_str = fs->ToString(config_options_);
std::string mismatch;
ASSERT_OK(FileSystem::CreateFromString(config_options_, opts_str, &copy));
ASSERT_TRUE(fs->AreEquivalent(config_options_, copy.get(), &mismatch));
ASSERT_OK(FileSystem::CreateFromString(
config_options_,
std::string("id=") + TimedFileSystem::kClassName() +
"; target=" + ReadOnlyFileSystem::kClassName(),
&fs));
ASSERT_NE(fs, nullptr);
opts_str = fs->ToString(config_options_);
ASSERT_STREQ(fs->Name(), TimedFileSystem::kClassName());
ASSERT_NE(fs->Inner(), nullptr);
ASSERT_STREQ(fs->Inner()->Name(), ReadOnlyFileSystem::kClassName());
ASSERT_EQ(fs->Inner()->Inner(), FileSystem::Default().get());
ASSERT_OK(FileSystem::CreateFromString(config_options_, opts_str, &copy));
ASSERT_TRUE(fs->AreEquivalent(config_options_, copy.get(), &mismatch));
}
#ifndef OS_WIN
TEST_F(CreateEnvTest, CreateChrootFileSystem) {
std::shared_ptr<FileSystem> fs, copy;
auto tmp_dir = test::TmpDir(Env::Default());
// The Chroot FileSystem has a required "chroot_dir" option.
ASSERT_NOK(FileSystem::CreateFromString(config_options_,
ChrootFileSystem::kClassName(), &fs));
// ChrootFileSystem fails with an invalid directory
ASSERT_NOK(FileSystem::CreateFromString(
config_options_,
std::string("chroot_dir=/No/Such/Directory; id=") +
ChrootFileSystem::kClassName(),
&fs));
std::string chroot_opts = std::string("chroot_dir=") + tmp_dir +
std::string("; id=") +
ChrootFileSystem::kClassName();
// Create a valid ChrootFileSystem with an inner Default
ASSERT_OK(FileSystem::CreateFromString(config_options_, chroot_opts, &fs));
ASSERT_NE(fs, nullptr);
ASSERT_STREQ(fs->Name(), ChrootFileSystem::kClassName());
ASSERT_EQ(fs->Inner(), FileSystem::Default().get());
std::string opts_str = fs->ToString(config_options_);
std::string mismatch;
ASSERT_OK(FileSystem::CreateFromString(config_options_, opts_str, &copy));
ASSERT_TRUE(fs->AreEquivalent(config_options_, copy.get(), &mismatch));
// Create a valid ChrootFileSystem with an inner TimedFileSystem
ASSERT_OK(FileSystem::CreateFromString(
config_options_,
chroot_opts + "; target=" + TimedFileSystem::kClassName(), &fs));
ASSERT_NE(fs, nullptr);
ASSERT_STREQ(fs->Name(), ChrootFileSystem::kClassName());
ASSERT_NE(fs->Inner(), nullptr);
ASSERT_STREQ(fs->Inner()->Name(), TimedFileSystem::kClassName());
ASSERT_EQ(fs->Inner()->Inner(), FileSystem::Default().get());
opts_str = fs->ToString(config_options_);
ASSERT_OK(FileSystem::CreateFromString(config_options_, opts_str, &copy));
ASSERT_TRUE(fs->AreEquivalent(config_options_, copy.get(), &mismatch));
// Create a TimedFileSystem with an inner ChrootFileSystem
ASSERT_OK(FileSystem::CreateFromString(
config_options_,
"target={" + chroot_opts + "}; id=" + TimedFileSystem::kClassName(),
&fs));
ASSERT_NE(fs, nullptr);
ASSERT_STREQ(fs->Name(), TimedFileSystem::kClassName());
ASSERT_NE(fs->Inner(), nullptr);
ASSERT_STREQ(fs->Inner()->Name(), ChrootFileSystem::kClassName());
ASSERT_EQ(fs->Inner()->Inner(), FileSystem::Default().get());
opts_str = fs->ToString(config_options_);
ASSERT_OK(FileSystem::CreateFromString(config_options_, opts_str, &copy));
ASSERT_TRUE(fs->AreEquivalent(config_options_, copy.get(), &mismatch));
}
#endif // OS_WIN
TEST_F(CreateEnvTest, CreateEncryptedFileSystem) {
std::shared_ptr<FileSystem> fs, copy;
std::string base_opts =
std::string("provider=1://test; id=") + EncryptedFileSystem::kClassName();
// The EncryptedFileSystem requires a "provider" option.
ASSERT_NOK(FileSystem::CreateFromString(
config_options_, EncryptedFileSystem::kClassName(), &fs));
ASSERT_OK(FileSystem::CreateFromString(config_options_, base_opts, &fs));
ASSERT_NE(fs, nullptr);
ASSERT_STREQ(fs->Name(), EncryptedFileSystem::kClassName());
ASSERT_EQ(fs->Inner(), FileSystem::Default().get());
std::string opts_str = fs->ToString(config_options_);
std::string mismatch;
ASSERT_OK(FileSystem::CreateFromString(config_options_, opts_str, &copy));
ASSERT_TRUE(fs->AreEquivalent(config_options_, copy.get(), &mismatch));
ASSERT_OK(FileSystem::CreateFromString(
config_options_, base_opts + "; target=" + TimedFileSystem::kClassName(),
&fs));
ASSERT_NE(fs, nullptr);
ASSERT_STREQ(fs->Name(), EncryptedFileSystem::kClassName());
ASSERT_NE(fs->Inner(), nullptr);
ASSERT_STREQ(fs->Inner()->Name(), TimedFileSystem::kClassName());
ASSERT_EQ(fs->Inner()->Inner(), FileSystem::Default().get());
opts_str = fs->ToString(config_options_);
ASSERT_OK(FileSystem::CreateFromString(config_options_, opts_str, &copy));
ASSERT_TRUE(fs->AreEquivalent(config_options_, copy.get(), &mismatch));
}
#endif // ROCKSDB_LITE #endif // ROCKSDB_LITE
namespace { namespace {
@ -2742,7 +2894,6 @@ TEST_F(EnvTest, FailureToCreateLockFile) {
// Clean up // Clean up
ASSERT_OK(DestroyDir(env, dir)); ASSERT_OK(DestroyDir(env, dir));
} }
} // namespace ROCKSDB_NAMESPACE } // namespace ROCKSDB_NAMESPACE
int main(int argc, char** argv) { int main(int argc, char** argv) {

112
env/file_system.cc vendored

@ -6,9 +6,17 @@
#include "rocksdb/file_system.h" #include "rocksdb/file_system.h"
#include "env/composite_env_wrapper.h" #include "env/composite_env_wrapper.h"
#include "env/env_chroot.h"
#include "env/env_encryption_ctr.h"
#include "env/fs_readonly.h"
#include "env/mock_env.h"
#include "options/db_options.h" #include "options/db_options.h"
#include "rocksdb/convenience.h" #include "rocksdb/convenience.h"
#include "rocksdb/utilities/customizable_util.h"
#include "rocksdb/utilities/object_registry.h" #include "rocksdb/utilities/object_registry.h"
#include "rocksdb/utilities/options_type.h"
#include "util/string_util.h"
#include "utilities/env_timed.h"
namespace ROCKSDB_NAMESPACE { namespace ROCKSDB_NAMESPACE {
@ -21,19 +29,63 @@ Status FileSystem::Load(const std::string& value,
return CreateFromString(ConfigOptions(), value, result); return CreateFromString(ConfigOptions(), value, result);
} }
#ifndef ROCKSDB_LITE
static int RegisterBuiltinFileSystems(ObjectLibrary& library,
const std::string& /*arg*/) {
library.Register<FileSystem>(
TimedFileSystem::kClassName(),
[](const std::string& /*uri*/, std::unique_ptr<FileSystem>* guard,
std::string* /* errmsg */) {
guard->reset(new TimedFileSystem(nullptr));
return guard->get();
});
library.Register<FileSystem>(
ReadOnlyFileSystem::kClassName(),
[](const std::string& /*uri*/, std::unique_ptr<FileSystem>* guard,
std::string* /* errmsg */) {
guard->reset(new ReadOnlyFileSystem(nullptr));
return guard->get();
});
library.Register<FileSystem>(
EncryptedFileSystem::kClassName(),
[](const std::string& /*uri*/, std::unique_ptr<FileSystem>* guard,
std::string* errmsg) {
Status s = NewEncryptedFileSystemImpl(nullptr, nullptr, guard);
if (!s.ok()) {
*errmsg = s.ToString();
}
return guard->get();
});
#ifndef OS_WIN
library.Register<FileSystem>(
ChrootFileSystem::kClassName(),
[](const std::string& /*uri*/, std::unique_ptr<FileSystem>* guard,
std::string* /* errmsg */) {
guard->reset(new ChrootFileSystem(nullptr, ""));
return guard->get();
});
#endif // OS_WIN
size_t num_types;
return static_cast<int>(library.GetFactoryCount(&num_types));
}
#endif // ROCKSDB_LITE
Status FileSystem::CreateFromString(const ConfigOptions& config_options, Status FileSystem::CreateFromString(const ConfigOptions& config_options,
const std::string& value, const std::string& value,
std::shared_ptr<FileSystem>* result) { std::shared_ptr<FileSystem>* result) {
Status s; auto default_fs = FileSystem::Default();
if (default_fs->IsInstanceOf(value)) {
*result = default_fs;
return Status::OK();
} else {
#ifndef ROCKSDB_LITE #ifndef ROCKSDB_LITE
(void)config_options; static std::once_flag once;
s = ObjectRegistry::NewInstance()->NewSharedObject<FileSystem>(value, result); std::call_once(once, [&]() {
#else RegisterBuiltinFileSystems(*(ObjectLibrary::Default().get()), "");
(void)config_options; });
(void)result; #endif // ROCKSDB_LITE
s = Status::NotSupported("Cannot load FileSystem in LITE mode", value); return LoadSharedObject<FileSystem>(config_options, value, nullptr, result);
#endif }
return s;
} }
IOStatus FileSystem::ReuseWritableFile(const std::string& fname, IOStatus FileSystem::ReuseWritableFile(const std::string& fname,
@ -147,4 +199,46 @@ IOStatus ReadFileToString(FileSystem* fs, const std::string& fname,
return s; return s;
} }
namespace {
static std::unordered_map<std::string, OptionTypeInfo> fs_wrapper_type_info = {
#ifndef ROCKSDB_LITE
{"target",
OptionTypeInfo::AsCustomSharedPtr<FileSystem>(
0, OptionVerificationType::kByName, OptionTypeFlags::kDontSerialize)},
#endif // ROCKSDB_LITE
};
} // namespace
FileSystemWrapper::FileSystemWrapper(const std::shared_ptr<FileSystem>& t)
: target_(t) {
RegisterOptions("", &target_, &fs_wrapper_type_info);
}
Status FileSystemWrapper::PrepareOptions(const ConfigOptions& options) {
if (target_ == nullptr) {
target_ = FileSystem::Default();
}
return FileSystem::PrepareOptions(options);
}
#ifndef ROCKSDB_LITE
std::string FileSystemWrapper::SerializeOptions(
const ConfigOptions& config_options, const std::string& header) const {
auto parent = FileSystem::SerializeOptions(config_options, "");
if (config_options.IsShallow() || target_ == nullptr ||
target_->IsInstanceOf(FileSystem::kDefaultName())) {
return parent;
} else {
std::string result = header;
if (!StartsWith(parent, OptionTypeInfo::kIdPropName())) {
result.append(OptionTypeInfo::kIdPropName()).append("=");
}
result.append(parent);
if (!EndsWith(result, config_options.delimiter)) {
result.append(config_options.delimiter);
}
result.append("target=").append(target_->ToString(config_options));
return result;
}
}
#endif // ROCKSDB_LITE
} // namespace ROCKSDB_NAMESPACE } // namespace ROCKSDB_NAMESPACE

@ -27,6 +27,9 @@ class FileSystemTracingWrapper : public FileSystemWrapper {
~FileSystemTracingWrapper() override {} ~FileSystemTracingWrapper() override {}
static const char* kClassName() { return "FileSystemTracing"; }
const char* Name() const override { return kClassName(); }
IOStatus NewSequentialFile(const std::string& fname, IOStatus NewSequentialFile(const std::string& fname,
const FileOptions& file_opts, const FileOptions& file_opts,
std::unique_ptr<FSSequentialFile>* result, std::unique_ptr<FSSequentialFile>* result,

14
env/fs_posix.cc vendored

@ -141,7 +141,9 @@ class PosixFileSystem : public FileSystem {
public: public:
PosixFileSystem(); PosixFileSystem();
const char* Name() const override { return "Posix File System"; } static const char* kClassName() { return "PosixFileSystem"; }
const char* Name() const override { return kClassName(); }
const char* NickName() const override { return kDefaultName(); }
~PosixFileSystem() override {} ~PosixFileSystem() override {}
@ -618,7 +620,8 @@ class PosixFileSystem : public FileSystem {
return IOStatus::NotFound(); return IOStatus::NotFound();
default: default:
assert(err == EIO || err == ENOMEM); assert(err == EIO || err == ENOMEM);
return IOStatus::IOError("Unexpected error(" + ToString(err) + return IOStatus::IOError("Unexpected error(" +
ROCKSDB_NAMESPACE::ToString(err) +
") accessing file `" + fname + "' "); ") accessing file `" + fname + "' ");
} }
} }
@ -819,10 +822,11 @@ class PosixFileSystem : public FileSystem {
errno = ENOLCK; errno = ENOLCK;
// Note that the thread ID printed is the same one as the one in // Note that the thread ID printed is the same one as the one in
// posix logger, but posix logger prints it hex format. // posix logger, but posix logger prints it hex format.
return IOError("lock hold by current process, acquire time " + return IOError(
ToString(prev_info.acquire_time) + "lock hold by current process, acquire time " +
ROCKSDB_NAMESPACE::ToString(prev_info.acquire_time) +
" acquiring thread " + " acquiring thread " +
ToString(prev_info.acquiring_thread), ROCKSDB_NAMESPACE::ToString(prev_info.acquiring_thread),
fname, errno); fname, errno);
} }

3
env/fs_readonly.h vendored

@ -26,6 +26,9 @@ class ReadOnlyFileSystem : public FileSystemWrapper {
explicit ReadOnlyFileSystem(const std::shared_ptr<FileSystem>& base) explicit ReadOnlyFileSystem(const std::shared_ptr<FileSystem>& base)
: FileSystemWrapper(base) {} : FileSystemWrapper(base) {}
static const char* kClassName() { return "ReadOnlyFileSystem"; }
const char* Name() const override { return kClassName(); }
IOStatus NewWritableFile(const std::string& /*fname*/, IOStatus NewWritableFile(const std::string& /*fname*/,
const FileOptions& /*options*/, const FileOptions& /*options*/,
std::unique_ptr<FSWritableFile>* /*result*/, std::unique_ptr<FSWritableFile>* /*result*/,

8
env/fs_remap.h vendored

@ -39,6 +39,14 @@ class RemapFileSystem : public FileSystemWrapper {
public: public:
// Left abstract: // Left abstract:
// const char* Name() const override { ... } // const char* Name() const override { ... }
static const char* kClassName() { return "RemapFileSystem"; }
bool IsInstanceOf(const std::string& id) const override {
if (id == kClassName()) {
return true;
} else {
return FileSystemWrapper::IsInstanceOf(id);
}
}
Status RegisterDbPaths(const std::vector<std::string>& paths) override; Status RegisterDbPaths(const std::vector<std::string>& paths) override;

107
env/mock_env.cc vendored

@ -565,93 +565,23 @@ class TestMemLogger : public Logger {
} }
size_t GetLogFileSize() const override { return log_size_; } size_t GetLogFileSize() const override { return log_size_; }
}; };
} // namespace
class MockFileSystem : public FileSystem { MockFileSystem::MockFileSystem(const std::shared_ptr<SystemClock>& clock,
public: bool supports_direct_io)
explicit MockFileSystem(const std::shared_ptr<SystemClock>& clock,
bool supports_direct_io = true)
: system_clock_(clock), supports_direct_io_(supports_direct_io) { : system_clock_(clock), supports_direct_io_(supports_direct_io) {
clock_ = system_clock_.get(); clock_ = system_clock_.get();
} }
~MockFileSystem() override { MockFileSystem::~MockFileSystem() {
for (auto i = file_map_.begin(); i != file_map_.end(); ++i) { for (auto i = file_map_.begin(); i != file_map_.end(); ++i) {
i->second->Unref(); i->second->Unref();
} }
} }
IOStatus MockFileSystem::GetAbsolutePath(const std::string& db_path,
const char* Name() const override { return "Memory"; }
IOStatus NewSequentialFile(const std::string& f, const FileOptions& file_opts,
std::unique_ptr<FSSequentialFile>* r,
IODebugContext* dbg) override;
IOStatus NewRandomAccessFile(const std::string& f,
const FileOptions& file_opts,
std::unique_ptr<FSRandomAccessFile>* r,
IODebugContext* dbg) override;
IOStatus NewRandomRWFile(const std::string& fname,
const FileOptions& file_opts,
std::unique_ptr<FSRandomRWFile>* result,
IODebugContext* dbg) override;
IOStatus ReuseWritableFile(const std::string& fname,
const std::string& old_fname,
const FileOptions& file_opts,
std::unique_ptr<FSWritableFile>* result,
IODebugContext* dbg) override;
IOStatus NewWritableFile(const std::string& fname,
const FileOptions& file_opts,
std::unique_ptr<FSWritableFile>* result,
IODebugContext* dbg) override;
IOStatus ReopenWritableFile(const std::string& fname,
const FileOptions& options,
std::unique_ptr<FSWritableFile>* result,
IODebugContext* dbg) override;
IOStatus NewDirectory(const std::string& /*name*/, const IOOptions& io_opts,
std::unique_ptr<FSDirectory>* result,
IODebugContext* dbg) override;
IOStatus FileExists(const std::string& fname, const IOOptions& /*io_opts*/,
IODebugContext* /*dbg*/) override;
IOStatus GetChildren(const std::string& dir, const IOOptions& options,
std::vector<std::string>* result,
IODebugContext* dbg) override;
IOStatus DeleteFile(const std::string& fname, const IOOptions& options,
IODebugContext* dbg) override;
IOStatus Truncate(const std::string& fname, size_t size,
const IOOptions& options, IODebugContext* dbg) override;
IOStatus CreateDir(const std::string& dirname, const IOOptions& options,
IODebugContext* dbg) override;
IOStatus CreateDirIfMissing(const std::string& dirname,
const IOOptions& options,
IODebugContext* dbg) override;
IOStatus DeleteDir(const std::string& dirname, const IOOptions& options,
IODebugContext* dbg) override;
IOStatus GetFileSize(const std::string& fname, const IOOptions& options,
uint64_t* file_size, IODebugContext* dbg) override;
IOStatus GetFileModificationTime(const std::string& fname,
const IOOptions& options,
uint64_t* file_mtime,
IODebugContext* dbg) override;
IOStatus RenameFile(const std::string& src, const std::string& target,
const IOOptions& options, IODebugContext* dbg) override;
IOStatus LinkFile(const std::string& /*src*/, const std::string& /*target*/,
const IOOptions& /*options*/,
IODebugContext* /*dbg*/) override;
IOStatus LockFile(const std::string& fname, const IOOptions& options,
FileLock** lock, IODebugContext* dbg) override;
IOStatus UnlockFile(FileLock* lock, const IOOptions& options,
IODebugContext* dbg) override;
IOStatus GetTestDirectory(const IOOptions& options, std::string* path,
IODebugContext* dbg) override;
IOStatus NewLogger(const std::string& fname, const IOOptions& io_opts,
std::shared_ptr<Logger>* result,
IODebugContext* dbg) override;
// Get full directory name for this db.
IOStatus GetAbsolutePath(const std::string& db_path,
const IOOptions& /*options*/, const IOOptions& /*options*/,
std::string* output_path, std::string* output_path,
IODebugContext* /*dbg*/) override { IODebugContext* /*dbg*/) {
*output_path = NormalizeMockPath(db_path); *output_path = NormalizeMockPath(db_path);
if (output_path->at(0) != '/') { if (output_path->at(0) != '/') {
return IOStatus::NotSupported("GetAbsolutePath"); return IOStatus::NotSupported("GetAbsolutePath");
@ -659,21 +589,8 @@ class MockFileSystem : public FileSystem {
return IOStatus::OK(); return IOStatus::OK();
} }
} }
IOStatus IsDirectory(const std::string& /*path*/,
const IOOptions& /*options*/, bool* /*is_dir*/,
IODebugContext* /*dgb*/) override {
return IOStatus::NotSupported("IsDirectory");
}
Status CorruptBuffer(const std::string& fname);
private:
bool RenameFileInternal(const std::string& src, const std::string& dest);
void DeleteFileInternal(const std::string& fname);
bool GetChildrenInternal(const std::string& fname,
std::vector<std::string>* results);
std::string NormalizeMockPath(const std::string& path) { std::string MockFileSystem::NormalizeMockPath(const std::string& path) {
std::string p = NormalizePath(path); std::string p = NormalizePath(path);
if (p.back() == kFilePathSeparator && p.size() > 1) { if (p.back() == kFilePathSeparator && p.size() > 1) {
p.pop_back(); p.pop_back();
@ -681,16 +598,6 @@ class MockFileSystem : public FileSystem {
return p; return p;
} }
private:
// Map from filenames to MemFile objects, representing a simple file system.
port::Mutex mutex_;
std::map<std::string, MemFile*> file_map_; // Protected by mutex_.
std::shared_ptr<SystemClock> system_clock_;
SystemClock* clock_;
bool supports_direct_io_;
};
} // Anonymous namespace
// Partial implementation of the FileSystem interface. // Partial implementation of the FileSystem interface.
IOStatus MockFileSystem::NewSequentialFile( IOStatus MockFileSystem::NewSequentialFile(
const std::string& fname, const FileOptions& file_opts, const std::string& fname, const FileOptions& file_opts,

106
env/mock_env.h vendored

@ -14,11 +14,117 @@
#include <vector> #include <vector>
#include "env/composite_env_wrapper.h" #include "env/composite_env_wrapper.h"
#include "port/port.h"
#include "rocksdb/env.h" #include "rocksdb/env.h"
#include "rocksdb/status.h" #include "rocksdb/status.h"
#include "rocksdb/system_clock.h" #include "rocksdb/system_clock.h"
namespace ROCKSDB_NAMESPACE { namespace ROCKSDB_NAMESPACE {
class MemFile;
class MockFileSystem : public FileSystem {
public:
explicit MockFileSystem(const std::shared_ptr<SystemClock>& clock,
bool supports_direct_io = true);
~MockFileSystem() override;
static const char* kClassName() { return "MemoryFileSystem"; }
const char* Name() const override { return kClassName(); }
IOStatus NewSequentialFile(const std::string& f, const FileOptions& file_opts,
std::unique_ptr<FSSequentialFile>* r,
IODebugContext* dbg) override;
IOStatus NewRandomAccessFile(const std::string& f,
const FileOptions& file_opts,
std::unique_ptr<FSRandomAccessFile>* r,
IODebugContext* dbg) override;
IOStatus NewRandomRWFile(const std::string& fname,
const FileOptions& file_opts,
std::unique_ptr<FSRandomRWFile>* result,
IODebugContext* dbg) override;
IOStatus ReuseWritableFile(const std::string& fname,
const std::string& old_fname,
const FileOptions& file_opts,
std::unique_ptr<FSWritableFile>* result,
IODebugContext* dbg) override;
IOStatus NewWritableFile(const std::string& fname,
const FileOptions& file_opts,
std::unique_ptr<FSWritableFile>* result,
IODebugContext* dbg) override;
IOStatus ReopenWritableFile(const std::string& fname,
const FileOptions& options,
std::unique_ptr<FSWritableFile>* result,
IODebugContext* dbg) override;
IOStatus NewDirectory(const std::string& /*name*/, const IOOptions& io_opts,
std::unique_ptr<FSDirectory>* result,
IODebugContext* dbg) override;
IOStatus FileExists(const std::string& fname, const IOOptions& /*io_opts*/,
IODebugContext* /*dbg*/) override;
IOStatus GetChildren(const std::string& dir, const IOOptions& options,
std::vector<std::string>* result,
IODebugContext* dbg) override;
IOStatus DeleteFile(const std::string& fname, const IOOptions& options,
IODebugContext* dbg) override;
IOStatus Truncate(const std::string& fname, size_t size,
const IOOptions& options, IODebugContext* dbg) override;
IOStatus CreateDir(const std::string& dirname, const IOOptions& options,
IODebugContext* dbg) override;
IOStatus CreateDirIfMissing(const std::string& dirname,
const IOOptions& options,
IODebugContext* dbg) override;
IOStatus DeleteDir(const std::string& dirname, const IOOptions& options,
IODebugContext* dbg) override;
IOStatus GetFileSize(const std::string& fname, const IOOptions& options,
uint64_t* file_size, IODebugContext* dbg) override;
IOStatus GetFileModificationTime(const std::string& fname,
const IOOptions& options,
uint64_t* file_mtime,
IODebugContext* dbg) override;
IOStatus RenameFile(const std::string& src, const std::string& target,
const IOOptions& options, IODebugContext* dbg) override;
IOStatus LinkFile(const std::string& /*src*/, const std::string& /*target*/,
const IOOptions& /*options*/,
IODebugContext* /*dbg*/) override;
IOStatus LockFile(const std::string& fname, const IOOptions& options,
FileLock** lock, IODebugContext* dbg) override;
IOStatus UnlockFile(FileLock* lock, const IOOptions& options,
IODebugContext* dbg) override;
IOStatus GetTestDirectory(const IOOptions& options, std::string* path,
IODebugContext* dbg) override;
IOStatus NewLogger(const std::string& fname, const IOOptions& io_opts,
std::shared_ptr<Logger>* result,
IODebugContext* dbg) override;
// Get full directory name for this db.
IOStatus GetAbsolutePath(const std::string& db_path,
const IOOptions& /*options*/,
std::string* output_path,
IODebugContext* /*dbg*/) override;
IOStatus IsDirectory(const std::string& /*path*/,
const IOOptions& /*options*/, bool* /*is_dir*/,
IODebugContext* /*dgb*/) override {
return IOStatus::NotSupported("IsDirectory");
}
Status CorruptBuffer(const std::string& fname);
private:
bool RenameFileInternal(const std::string& src, const std::string& dest);
void DeleteFileInternal(const std::string& fname);
bool GetChildrenInternal(const std::string& fname,
std::vector<std::string>* results);
std::string NormalizeMockPath(const std::string& path);
private:
// Map from filenames to MemFile objects, representing a simple file system.
port::Mutex mutex_;
std::map<std::string, MemFile*> file_map_; // Protected by mutex_.
std::shared_ptr<SystemClock> system_clock_;
SystemClock* clock_;
bool supports_direct_io_;
};
class MockEnv : public CompositeEnvWrapper { class MockEnv : public CompositeEnvWrapper {
public: public:
static MockEnv* Create(Env* base); static MockEnv* Create(Env* base);

@ -39,6 +39,9 @@ class MockFS : public FileSystemWrapper {
bool support_prefetch) bool support_prefetch)
: FileSystemWrapper(wrapped), support_prefetch_(support_prefetch) {} : FileSystemWrapper(wrapped), support_prefetch_(support_prefetch) {}
static const char* kClassName() { return "MockFS"; }
const char* Name() const override { return kClassName(); }
IOStatus NewRandomAccessFile(const std::string& fname, IOStatus NewRandomAccessFile(const std::string& fname,
const FileOptions& opts, const FileOptions& opts,
std::unique_ptr<FSRandomAccessFile>* result, std::unique_ptr<FSRandomAccessFile>* result,

@ -24,6 +24,9 @@ struct ConfigOptions;
// read from disk. // read from disk.
Env* NewEncryptedEnv(Env* base_env, Env* NewEncryptedEnv(Env* base_env,
const std::shared_ptr<EncryptionProvider>& provider); const std::shared_ptr<EncryptionProvider>& provider);
std::shared_ptr<FileSystem> NewEncryptedFS(
const std::shared_ptr<FileSystem>& base_fs,
const std::shared_ptr<EncryptionProvider>& provider);
// BlockAccessCipherStream is the base class for any cipher stream that // BlockAccessCipherStream is the base class for any cipher stream that
// supports random access at block level (without requiring data from other // supports random access at block level (without requiring data from other
@ -440,6 +443,14 @@ class EncryptedFileSystem : public FileSystemWrapper {
// otherwise // otherwise
virtual Status AddCipher(const std::string& descriptor, const char* cipher, virtual Status AddCipher(const std::string& descriptor, const char* cipher,
size_t len, bool for_write) = 0; size_t len, bool for_write) = 0;
static const char* kClassName() { return "EncryptedFileSystem"; }
bool IsInstanceOf(const std::string& name) const override {
if (name == kClassName()) {
return true;
} else {
return FileSystemWrapper::IsInstanceOf(name);
}
}
}; };
} // namespace ROCKSDB_NAMESPACE } // namespace ROCKSDB_NAMESPACE

@ -28,6 +28,7 @@
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
#include "rocksdb/customizable.h"
#include "rocksdb/env.h" #include "rocksdb/env.h"
#include "rocksdb/io_status.h" #include "rocksdb/io_status.h"
#include "rocksdb/options.h" #include "rocksdb/options.h"
@ -207,7 +208,8 @@ struct IODebugContext {
// retryable. // retryable.
// NewCompositeEnv can be used to create an Env with a custom FileSystem for // NewCompositeEnv can be used to create an Env with a custom FileSystem for
// DBOptions::env. // DBOptions::env.
class FileSystem {
class FileSystem : public Customizable {
public: public:
FileSystem(); FileSystem();
@ -216,9 +218,8 @@ class FileSystem {
virtual ~FileSystem(); virtual ~FileSystem();
virtual const char* Name() const = 0;
static const char* Type() { return "FileSystem"; } static const char* Type() { return "FileSystem"; }
static const char* kDefaultName() { return "DefaultFileSystem"; }
// Loads the FileSystem specified by the input value into the result // Loads the FileSystem specified by the input value into the result
// The CreateFromString alternative should be used; this method may be // The CreateFromString alternative should be used; this method may be
@ -1147,12 +1148,9 @@ class FSDirectory {
class FileSystemWrapper : public FileSystem { class FileSystemWrapper : public FileSystem {
public: public:
// Initialize an EnvWrapper that delegates all calls to *t // Initialize an EnvWrapper that delegates all calls to *t
explicit FileSystemWrapper(const std::shared_ptr<FileSystem>& t) explicit FileSystemWrapper(const std::shared_ptr<FileSystem>& t);
: target_(t) {}
~FileSystemWrapper() override {} ~FileSystemWrapper() override {}
const char* Name() const override { return target_->Name(); }
// Return the target to which this Env forwards all calls // Return the target to which this Env forwards all calls
FileSystem* target() const { return target_.get(); } FileSystem* target() const { return target_.get(); }
@ -1343,7 +1341,13 @@ class FileSystemWrapper : public FileSystem {
return target_->IsDirectory(path, options, is_dir, dbg); return target_->IsDirectory(path, options, is_dir, dbg);
} }
private: const Customizable* Inner() const override { return target_.get(); }
Status PrepareOptions(const ConfigOptions& options) override;
#ifndef ROCKSDB_LITE
std::string SerializeOptions(const ConfigOptions& config_options,
const std::string& header) const override;
#endif // ROCKSDB_LITE
protected:
std::shared_ptr<FileSystem> target_; std::shared_ptr<FileSystem> target_;
}; };

@ -1299,6 +1299,17 @@ class MockCipher : public BlockCipher {
Status Encrypt(char* /*data*/) override { return Status::NotSupported(); } Status Encrypt(char* /*data*/) override { return Status::NotSupported(); }
Status Decrypt(char* data) override { return Encrypt(data); } Status Decrypt(char* data) override { return Encrypt(data); }
}; };
#endif // ROCKSDB_LITE
class DummyFileSystem : public FileSystemWrapper {
public:
explicit DummyFileSystem(const std::shared_ptr<FileSystem>& t)
: FileSystemWrapper(t) {}
static const char* kClassName() { return "DummyFileSystem"; }
const char* Name() const override { return kClassName(); }
};
#ifndef ROCKSDB_LITE
#endif // ROCKSDB_LITE #endif // ROCKSDB_LITE
@ -1406,6 +1417,14 @@ static int RegisterLocalObjects(ObjectLibrary& library,
return guard->get(); return guard->get();
}); });
library.Register<FileSystem>(
DummyFileSystem::kClassName(),
[](const std::string& /*uri*/, std::unique_ptr<FileSystem>* guard,
std::string* /* errmsg */) {
guard->reset(new DummyFileSystem(nullptr));
return guard->get();
});
library.Register<SstPartitionerFactory>( library.Register<SstPartitionerFactory>(
MockSstPartitionerFactory::kClassName(), MockSstPartitionerFactory::kClassName(),
[](const std::string& /*uri*/, [](const std::string& /*uri*/,
@ -1495,6 +1514,24 @@ TEST_F(LoadCustomizableTest, LoadTableFactoryTest) {
} }
} }
TEST_F(LoadCustomizableTest, LoadFileSystemTest) {
ColumnFamilyOptions cf_opts;
std::shared_ptr<FileSystem> result;
ASSERT_NOK(FileSystem::CreateFromString(
config_options_, DummyFileSystem::kClassName(), &result));
ASSERT_OK(FileSystem::CreateFromString(config_options_,
FileSystem::kDefaultName(), &result));
ASSERT_NE(result, nullptr);
ASSERT_TRUE(result->IsInstanceOf(FileSystem::kDefaultName()));
if (RegisterTests("Test")) {
ASSERT_OK(FileSystem::CreateFromString(
config_options_, DummyFileSystem::kClassName(), &result));
ASSERT_NE(result, nullptr);
ASSERT_STREQ(result->Name(), DummyFileSystem::kClassName());
ASSERT_FALSE(result->IsInstanceOf(FileSystem::kDefaultName()));
}
}
TEST_F(LoadCustomizableTest, LoadSecondaryCacheTest) { TEST_F(LoadCustomizableTest, LoadSecondaryCacheTest) {
std::shared_ptr<SecondaryCache> result; std::shared_ptr<SecondaryCache> result;
ASSERT_NOK(SecondaryCache::CreateFromString( ASSERT_NOK(SecondaryCache::CreateFromString(

@ -110,7 +110,10 @@ class WinFileSystem : public FileSystem {
static const std::shared_ptr<WinFileSystem>& Default(); static const std::shared_ptr<WinFileSystem>& Default();
WinFileSystem(const std::shared_ptr<SystemClock>& clock); WinFileSystem(const std::shared_ptr<SystemClock>& clock);
~WinFileSystem() {} ~WinFileSystem() {}
const char* Name() const { return "WinFS"; } static const char* kClassName() { return "WinFS"; }
const char* Name() const override { return kClassName(); }
const char* NickName() const { return kDefaultName(); }
static size_t GetSectorSize(const std::string& fname); static size_t GetSectorSize(const std::string& fname);
size_t GetPageSize() const { return page_size_; } size_t GetPageSize() const { return page_size_; }
size_t GetAllocationGranularity() const { return allocation_granularity_; } size_t GetAllocationGranularity() const { return allocation_granularity_; }

@ -555,6 +555,9 @@ class StringFS : public FileSystemWrapper {
: FileSystemWrapper(t) {} : FileSystemWrapper(t) {}
~StringFS() override {} ~StringFS() override {}
static const char* kClassName() { return "StringFS"; }
const char* Name() const override { return kClassName(); }
const std::string& GetContent(const std::string& f) { return files_[f]; } const std::string& GetContent(const std::string& f) { return files_[f]; }
const IOStatus WriteToNewFile(const std::string& file_name, const IOStatus WriteToNewFile(const std::string& file_name,

@ -2,6 +2,7 @@
// This source code is licensed under both the GPLv2 (found in the // This source code is licensed under both the GPLv2 (found in the
// COPYING file in the root directory) and Apache 2.0 License // COPYING file in the root directory) and Apache 2.0 License
// (found in the LICENSE.Apache file in the root directory). // (found in the LICENSE.Apache file in the root directory).
#include "utilities/env_timed.h"
#include "env/composite_env_wrapper.h" #include "env/composite_env_wrapper.h"
#include "monitoring/perf_context_imp.h" #include "monitoring/perf_context_imp.h"
@ -12,154 +13,157 @@
namespace ROCKSDB_NAMESPACE { namespace ROCKSDB_NAMESPACE {
#ifndef ROCKSDB_LITE #ifndef ROCKSDB_LITE
namespace { TimedFileSystem::TimedFileSystem(const std::shared_ptr<FileSystem>& base)
class TimedFileSystem : public FileSystemWrapper {
public:
explicit TimedFileSystem(const std::shared_ptr<FileSystem>& base)
: FileSystemWrapper(base) {} : FileSystemWrapper(base) {}
IOStatus TimedFileSystem::NewSequentialFile(
const char* Name() const override { return "TimedFS"; } const std::string& fname, const FileOptions& options,
IOStatus NewSequentialFile(const std::string& fname, std::unique_ptr<FSSequentialFile>* result, IODebugContext* dbg) {
const FileOptions& options,
std::unique_ptr<FSSequentialFile>* result,
IODebugContext* dbg) override {
PERF_TIMER_GUARD(env_new_sequential_file_nanos); PERF_TIMER_GUARD(env_new_sequential_file_nanos);
return FileSystemWrapper::NewSequentialFile(fname, options, result, dbg); return FileSystemWrapper::NewSequentialFile(fname, options, result, dbg);
} }
IOStatus NewRandomAccessFile(const std::string& fname, IOStatus TimedFileSystem::NewRandomAccessFile(
const FileOptions& options, const std::string& fname, const FileOptions& options,
std::unique_ptr<FSRandomAccessFile>* result, std::unique_ptr<FSRandomAccessFile>* result, IODebugContext* dbg) {
IODebugContext* dbg) override {
PERF_TIMER_GUARD(env_new_random_access_file_nanos); PERF_TIMER_GUARD(env_new_random_access_file_nanos);
return FileSystemWrapper::NewRandomAccessFile(fname, options, result, dbg); return FileSystemWrapper::NewRandomAccessFile(fname, options, result, dbg);
} }
IOStatus NewWritableFile(const std::string& fname, const FileOptions& options, IOStatus TimedFileSystem::NewWritableFile(
std::unique_ptr<FSWritableFile>* result, const std::string& fname, const FileOptions& options,
IODebugContext* dbg) override { std::unique_ptr<FSWritableFile>* result, IODebugContext* dbg) {
PERF_TIMER_GUARD(env_new_writable_file_nanos); PERF_TIMER_GUARD(env_new_writable_file_nanos);
return FileSystemWrapper::NewWritableFile(fname, options, result, dbg); return FileSystemWrapper::NewWritableFile(fname, options, result, dbg);
} }
IOStatus ReuseWritableFile(const std::string& fname, IOStatus TimedFileSystem::ReuseWritableFile(
const std::string& old_fname, const std::string& fname, const std::string& old_fname,
const FileOptions& options, const FileOptions& options, std::unique_ptr<FSWritableFile>* result,
std::unique_ptr<FSWritableFile>* result, IODebugContext* dbg) {
IODebugContext* dbg) override {
PERF_TIMER_GUARD(env_reuse_writable_file_nanos); PERF_TIMER_GUARD(env_reuse_writable_file_nanos);
return FileSystemWrapper::ReuseWritableFile(fname, old_fname, options, return FileSystemWrapper::ReuseWritableFile(fname, old_fname, options, result,
result, dbg); dbg);
} }
IOStatus NewRandomRWFile(const std::string& fname, const FileOptions& options, IOStatus TimedFileSystem::NewRandomRWFile(
std::unique_ptr<FSRandomRWFile>* result, const std::string& fname, const FileOptions& options,
IODebugContext* dbg) override { std::unique_ptr<FSRandomRWFile>* result, IODebugContext* dbg) {
PERF_TIMER_GUARD(env_new_random_rw_file_nanos); PERF_TIMER_GUARD(env_new_random_rw_file_nanos);
return FileSystemWrapper::NewRandomRWFile(fname, options, result, dbg); return FileSystemWrapper::NewRandomRWFile(fname, options, result, dbg);
} }
IOStatus NewDirectory(const std::string& name, const IOOptions& options, IOStatus TimedFileSystem::NewDirectory(const std::string& name,
const IOOptions& options,
std::unique_ptr<FSDirectory>* result, std::unique_ptr<FSDirectory>* result,
IODebugContext* dbg) override { IODebugContext* dbg) {
PERF_TIMER_GUARD(env_new_directory_nanos); PERF_TIMER_GUARD(env_new_directory_nanos);
return FileSystemWrapper::NewDirectory(name, options, result, dbg); return FileSystemWrapper::NewDirectory(name, options, result, dbg);
} }
IOStatus FileExists(const std::string& fname, const IOOptions& options, IOStatus TimedFileSystem::FileExists(const std::string& fname,
IODebugContext* dbg) override { const IOOptions& options,
IODebugContext* dbg) {
PERF_TIMER_GUARD(env_file_exists_nanos); PERF_TIMER_GUARD(env_file_exists_nanos);
return FileSystemWrapper::FileExists(fname, options, dbg); return FileSystemWrapper::FileExists(fname, options, dbg);
} }
IOStatus GetChildren(const std::string& dir, const IOOptions& options, IOStatus TimedFileSystem::GetChildren(const std::string& dir,
const IOOptions& options,
std::vector<std::string>* result, std::vector<std::string>* result,
IODebugContext* dbg) override { IODebugContext* dbg) {
PERF_TIMER_GUARD(env_get_children_nanos); PERF_TIMER_GUARD(env_get_children_nanos);
return FileSystemWrapper::GetChildren(dir, options, result, dbg); return FileSystemWrapper::GetChildren(dir, options, result, dbg);
} }
IOStatus GetChildrenFileAttributes(const std::string& dir, IOStatus TimedFileSystem::GetChildrenFileAttributes(
const IOOptions& options, const std::string& dir, const IOOptions& options,
std::vector<FileAttributes>* result, std::vector<FileAttributes>* result, IODebugContext* dbg) {
IODebugContext* dbg) override {
PERF_TIMER_GUARD(env_get_children_file_attributes_nanos); PERF_TIMER_GUARD(env_get_children_file_attributes_nanos);
return FileSystemWrapper::GetChildrenFileAttributes(dir, options, result, return FileSystemWrapper::GetChildrenFileAttributes(dir, options, result,
dbg); dbg);
} }
IOStatus DeleteFile(const std::string& fname, const IOOptions& options, IOStatus TimedFileSystem::DeleteFile(const std::string& fname,
IODebugContext* dbg) override { const IOOptions& options,
IODebugContext* dbg) {
PERF_TIMER_GUARD(env_delete_file_nanos); PERF_TIMER_GUARD(env_delete_file_nanos);
return FileSystemWrapper::DeleteFile(fname, options, dbg); return FileSystemWrapper::DeleteFile(fname, options, dbg);
} }
IOStatus CreateDir(const std::string& dirname, const IOOptions& options, IOStatus TimedFileSystem::CreateDir(const std::string& dirname,
IODebugContext* dbg) override { const IOOptions& options,
IODebugContext* dbg) {
PERF_TIMER_GUARD(env_create_dir_nanos); PERF_TIMER_GUARD(env_create_dir_nanos);
return FileSystemWrapper::CreateDir(dirname, options, dbg); return FileSystemWrapper::CreateDir(dirname, options, dbg);
} }
IOStatus CreateDirIfMissing(const std::string& dirname, IOStatus TimedFileSystem::CreateDirIfMissing(const std::string& dirname,
const IOOptions& options, const IOOptions& options,
IODebugContext* dbg) override { IODebugContext* dbg) {
PERF_TIMER_GUARD(env_create_dir_if_missing_nanos); PERF_TIMER_GUARD(env_create_dir_if_missing_nanos);
return FileSystemWrapper::CreateDirIfMissing(dirname, options, dbg); return FileSystemWrapper::CreateDirIfMissing(dirname, options, dbg);
} }
IOStatus DeleteDir(const std::string& dirname, const IOOptions& options, IOStatus TimedFileSystem::DeleteDir(const std::string& dirname,
IODebugContext* dbg) override { const IOOptions& options,
IODebugContext* dbg) {
PERF_TIMER_GUARD(env_delete_dir_nanos); PERF_TIMER_GUARD(env_delete_dir_nanos);
return FileSystemWrapper::DeleteDir(dirname, options, dbg); return FileSystemWrapper::DeleteDir(dirname, options, dbg);
} }
IOStatus GetFileSize(const std::string& fname, const IOOptions& options, IOStatus TimedFileSystem::GetFileSize(const std::string& fname,
uint64_t* file_size, IODebugContext* dbg) override { const IOOptions& options,
uint64_t* file_size,
IODebugContext* dbg) {
PERF_TIMER_GUARD(env_get_file_size_nanos); PERF_TIMER_GUARD(env_get_file_size_nanos);
return FileSystemWrapper::GetFileSize(fname, options, file_size, dbg); return FileSystemWrapper::GetFileSize(fname, options, file_size, dbg);
} }
IOStatus GetFileModificationTime(const std::string& fname, IOStatus TimedFileSystem::GetFileModificationTime(const std::string& fname,
const IOOptions& options, const IOOptions& options,
uint64_t* file_mtime, uint64_t* file_mtime,
IODebugContext* dbg) override { IODebugContext* dbg) {
PERF_TIMER_GUARD(env_get_file_modification_time_nanos); PERF_TIMER_GUARD(env_get_file_modification_time_nanos);
return FileSystemWrapper::GetFileModificationTime(fname, options, return FileSystemWrapper::GetFileModificationTime(fname, options, file_mtime,
file_mtime, dbg); dbg);
} }
IOStatus RenameFile(const std::string& src, const std::string& dst, IOStatus TimedFileSystem::RenameFile(const std::string& src,
const IOOptions& options, IODebugContext* dbg) override { const std::string& dst,
const IOOptions& options,
IODebugContext* dbg) {
PERF_TIMER_GUARD(env_rename_file_nanos); PERF_TIMER_GUARD(env_rename_file_nanos);
return FileSystemWrapper::RenameFile(src, dst, options, dbg); return FileSystemWrapper::RenameFile(src, dst, options, dbg);
} }
IOStatus LinkFile(const std::string& src, const std::string& dst, IOStatus TimedFileSystem::LinkFile(const std::string& src,
const IOOptions& options, IODebugContext* dbg) override { const std::string& dst,
const IOOptions& options,
IODebugContext* dbg) {
PERF_TIMER_GUARD(env_link_file_nanos); PERF_TIMER_GUARD(env_link_file_nanos);
return FileSystemWrapper::LinkFile(src, dst, options, dbg); return FileSystemWrapper::LinkFile(src, dst, options, dbg);
} }
IOStatus LockFile(const std::string& fname, const IOOptions& options, IOStatus TimedFileSystem::LockFile(const std::string& fname,
FileLock** lock, IODebugContext* dbg) override { const IOOptions& options, FileLock** lock,
IODebugContext* dbg) {
PERF_TIMER_GUARD(env_lock_file_nanos); PERF_TIMER_GUARD(env_lock_file_nanos);
return FileSystemWrapper::LockFile(fname, options, lock, dbg); return FileSystemWrapper::LockFile(fname, options, lock, dbg);
} }
IOStatus UnlockFile(FileLock* lock, const IOOptions& options, IOStatus TimedFileSystem::UnlockFile(FileLock* lock, const IOOptions& options,
IODebugContext* dbg) override { IODebugContext* dbg) {
PERF_TIMER_GUARD(env_unlock_file_nanos); PERF_TIMER_GUARD(env_unlock_file_nanos);
return FileSystemWrapper::UnlockFile(lock, options, dbg); return FileSystemWrapper::UnlockFile(lock, options, dbg);
} }
IOStatus NewLogger(const std::string& fname, const IOOptions& options, IOStatus TimedFileSystem::NewLogger(const std::string& fname,
const IOOptions& options,
std::shared_ptr<Logger>* result, std::shared_ptr<Logger>* result,
IODebugContext* dbg) override { IODebugContext* dbg) {
PERF_TIMER_GUARD(env_new_logger_nanos); PERF_TIMER_GUARD(env_new_logger_nanos);
return FileSystemWrapper::NewLogger(fname, options, result, dbg); return FileSystemWrapper::NewLogger(fname, options, result, dbg);
} }
};
} // namespace
std::shared_ptr<FileSystem> NewTimedFileSystem( std::shared_ptr<FileSystem> NewTimedFileSystem(
const std::shared_ptr<FileSystem>& base) { const std::shared_ptr<FileSystem>& base) {

@ -0,0 +1,97 @@
// Copyright (c) 2019-present, Facebook, Inc. All rights reserved.
// This source code is licensed under both the GPLv2 (found in the
// COPYING file in the root directory) and Apache 2.0 License
// (found in the LICENSE.Apache file in the root directory).
//
#pragma once
#include "rocksdb/file_system.h"
namespace ROCKSDB_NAMESPACE {
#ifndef ROCKSDB_LITE
class TimedFileSystem : public FileSystemWrapper {
public:
explicit TimedFileSystem(const std::shared_ptr<FileSystem>& base);
static const char* kClassName() { return "TimedFS"; }
const char* Name() const override { return kClassName(); }
IOStatus NewSequentialFile(const std::string& fname,
const FileOptions& options,
std::unique_ptr<FSSequentialFile>* result,
IODebugContext* dbg) override;
IOStatus NewRandomAccessFile(const std::string& fname,
const FileOptions& options,
std::unique_ptr<FSRandomAccessFile>* result,
IODebugContext* dbg) override;
IOStatus NewWritableFile(const std::string& fname, const FileOptions& options,
std::unique_ptr<FSWritableFile>* result,
IODebugContext* dbg) override;
IOStatus ReuseWritableFile(const std::string& fname,
const std::string& old_fname,
const FileOptions& options,
std::unique_ptr<FSWritableFile>* result,
IODebugContext* dbg) override;
IOStatus NewRandomRWFile(const std::string& fname, const FileOptions& options,
std::unique_ptr<FSRandomRWFile>* result,
IODebugContext* dbg) override;
IOStatus NewDirectory(const std::string& name, const IOOptions& options,
std::unique_ptr<FSDirectory>* result,
IODebugContext* dbg) override;
IOStatus FileExists(const std::string& fname, const IOOptions& options,
IODebugContext* dbg) override;
IOStatus GetChildren(const std::string& dir, const IOOptions& options,
std::vector<std::string>* result,
IODebugContext* dbg) override;
IOStatus GetChildrenFileAttributes(const std::string& dir,
const IOOptions& options,
std::vector<FileAttributes>* result,
IODebugContext* dbg) override;
IOStatus DeleteFile(const std::string& fname, const IOOptions& options,
IODebugContext* dbg) override;
IOStatus CreateDir(const std::string& dirname, const IOOptions& options,
IODebugContext* dbg) override;
IOStatus CreateDirIfMissing(const std::string& dirname,
const IOOptions& options,
IODebugContext* dbg) override;
IOStatus DeleteDir(const std::string& dirname, const IOOptions& options,
IODebugContext* dbg) override;
IOStatus GetFileSize(const std::string& fname, const IOOptions& options,
uint64_t* file_size, IODebugContext* dbg) override;
IOStatus GetFileModificationTime(const std::string& fname,
const IOOptions& options,
uint64_t* file_mtime,
IODebugContext* dbg) override;
IOStatus RenameFile(const std::string& src, const std::string& dst,
const IOOptions& options, IODebugContext* dbg) override;
IOStatus LinkFile(const std::string& src, const std::string& dst,
const IOOptions& options, IODebugContext* dbg) override;
IOStatus LockFile(const std::string& fname, const IOOptions& options,
FileLock** lock, IODebugContext* dbg) override;
IOStatus UnlockFile(FileLock* lock, const IOOptions& options,
IODebugContext* dbg) override;
IOStatus NewLogger(const std::string& fname, const IOOptions& options,
std::shared_ptr<Logger>* result,
IODebugContext* dbg) override;
};
#endif // ROCKSDB_LITE
} // namespace ROCKSDB_NAMESPACE

@ -200,7 +200,8 @@ class FaultInjectionTestFS : public FileSystemWrapper {
fail_get_file_unique_id_(false) {} fail_get_file_unique_id_(false) {}
virtual ~FaultInjectionTestFS() { error_.PermitUncheckedError(); } virtual ~FaultInjectionTestFS() { error_.PermitUncheckedError(); }
const char* Name() const override { return "FaultInjectionTestFS"; } static const char* kClassName() { return "FaultInjectionTestFS"; }
const char* Name() const override { return kClassName(); }
IOStatus NewDirectory(const std::string& name, const IOOptions& options, IOStatus NewDirectory(const std::string& name, const IOOptions& options,
std::unique_ptr<FSDirectory>* result, std::unique_ptr<FSDirectory>* result,

Loading…
Cancel
Save