diff --git a/db/db_impl.cc b/db/db_impl.cc index f96d245..8b2ff0d 100644 --- a/db/db_impl.cc +++ b/db/db_impl.cc @@ -295,7 +295,7 @@ Status DBImpl::Recover(VersionEdit* edit, bool* save_manifest) { // Ignore error from CreateDir since the creation of the DB is // committed only when the descriptor is created, and this directory // may already exist from a previous failed creation attempt. - env_->CreateDir(dbname_); + env_->CreateDirIteratively(dbname_); assert(db_lock_ == nullptr); Status s = env_->LockFile(LockFileName(dbname_), &db_lock_); if (!s.ok()) { diff --git a/include/leveldb/env.h b/include/leveldb/env.h index e00895a..1d8e86f 100644 --- a/include/leveldb/env.h +++ b/include/leveldb/env.h @@ -20,6 +20,7 @@ #include "leveldb/export.h" #include "leveldb/status.h" +#include "util/dir_helper.h" // This workaround can be removed when leveldb::Env::DeleteFile is removed. #if defined(_WIN32) @@ -113,6 +114,9 @@ class LEVELDB_EXPORT Env { // Returns true iff the named file exists. virtual bool FileExists(const std::string& fname) = 0; + // Returns true if specified director path exists. + virtual bool DirectoryExists(const std::string& dir_path) = 0; + // Store in *result the names of the children of the specified directory. // The names are relative to "dir". // Original contents of *results are dropped. @@ -216,6 +220,9 @@ class LEVELDB_EXPORT Env { // Sleep/delay the thread for the prescribed number of micro-seconds. virtual void SleepForMicroseconds(int micros) = 0; + + // Create folder with specified subfolders recursively + virtual Status CreateDirIteratively(const std::string& path) = 0; }; // A file abstraction for reading sequentially through a file @@ -358,6 +365,9 @@ class LEVELDB_EXPORT EnvWrapper : public Env { bool FileExists(const std::string& f) override { return target_->FileExists(f); } + bool DirectoryExists(const std::string& d) override { + return target_->DirectoryExists(d); + } Status GetChildren(const std::string& dir, std::vector* r) override { return target_->GetChildren(dir, r); @@ -368,6 +378,9 @@ class LEVELDB_EXPORT EnvWrapper : public Env { Status CreateDir(const std::string& d) override { return target_->CreateDir(d); } + Status CreateDirIteratively(const std::string& d) override { + return target_->CreateDirIteratively(d); + } Status RemoveDir(const std::string& d) override { return target_->RemoveDir(d); } diff --git a/util/dir_helper.h b/util/dir_helper.h new file mode 100644 index 0000000..03a518e --- /dev/null +++ b/util/dir_helper.h @@ -0,0 +1,41 @@ +#ifndef STORAGE_LEVELDB_UTIL_DIR_HELPER_H_ +#define STORAGE_LEVELDB_UTIL_DIR_HELPER_H_ + +#include +#include + +namespace leveldb { + +inline bool IsAbsolute(const std::string& dirpath) { + return ( + dirpath.size() >= 3 && + (std::toupper(dirpath[0]) >= 'A' && std::toupper(dirpath[0]) <= 'Z') && + dirpath[1] == ':'); +} + +inline bool IsDirectorySeparator(const char c) { + return (c == '/' || c == '\\'); +} + +inline std::string GetNormalizedDirectoryPath(const std::string& dirpath) { + std::string path = dirpath; + size_t remove_pos = 0; + + if (!IsAbsolute(path)) { + remove_pos = path.find_first_not_of(".\\/"); + if (remove_pos != std::string::npos) { + path.erase(0, remove_pos); + } + } + if (IsDirectorySeparator(path[path.size() - 1])) { + remove_pos = path.find_last_not_of(".\\/"); + if (remove_pos != std::string::npos) { + path.erase(remove_pos + 1); + } + } + return path; +} + +} // namespace leveldb + +#endif \ No newline at end of file diff --git a/util/env_windows.cc b/util/env_windows.cc index ae5149a..6aa7b48 100644 --- a/util/env_windows.cc +++ b/util/env_windows.cc @@ -23,6 +23,7 @@ #include #include #include +#include #include "leveldb/env.h" #include "leveldb/slice.h" @@ -492,6 +493,11 @@ class WindowsEnv : public Env { return GetFileAttributesA(filename.c_str()) != INVALID_FILE_ATTRIBUTES; } + bool DirectoryExists(const std::string& dir_path) override { + DWORD attrs = GetFileAttributesA(dir_path.c_str()); + return attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY); + } + Status GetChildren(const std::string& directory_path, std::vector* result) override { const std::string find_pattern = directory_path + "\\*"; @@ -535,6 +541,43 @@ class WindowsEnv : public Env { return Status::OK(); } + Status CreateDirIteratively(const std::string& path) override { + if (DirectoryExists(path)) { + return Status::OK(); + } + + std::string& dirpath = GetNormalizedDirectoryPath(path); + + std::stack dirs; + bool dir_exists = false; + int root_length = IsAbsolute(dirpath) ? 2 : 0; + int path_length = dirpath.size() - 1; + + while (path_length >= root_length && !dir_exists) { + std::string dir = dirpath.substr(0, path_length + 1); + + if (!DirectoryExists(dir)) { + dirs.push(std::move(dir)); + } else { + dir_exists = true; + } + + while (path_length > root_length && !IsDirectorySeparator(dirpath[path_length])) { + --path_length; + } + --path_length; + } + while (!dirs.empty()) { + const std::string dir_to_create = dirs.top(); + dirs.pop(); + + if (!::CreateDirectoryA(dir_to_create.c_str(), nullptr)) { + return WindowsError(dir_to_create, ::GetLastError()); + } + } + return Status::OK(); + } + Status RemoveDir(const std::string& dirname) override { if (!::RemoveDirectoryA(dirname.c_str())) { return WindowsError(dirname, ::GetLastError());