diff --git a/db/db_impl.cc b/db/db_impl.cc index 2c4779e..3fef554 100644 --- a/db/db_impl.cc +++ b/db/db_impl.cc @@ -179,7 +179,7 @@ DBImpl::~DBImpl() { if (owns_cache_) { delete options_.block_cache; } - if (path_ != nullptr) { + if (path_) { delete path_; } } @@ -301,7 +301,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(path_->Name()); + env_->CreateDir(path_->ToString()); assert(db_lock_ == nullptr); Status s = env_->LockFile(LockFileName(dbname_), &db_lock_); if (!s.ok()) { diff --git a/include/leveldb/db_path.h b/include/leveldb/db_path.h index 83b0f44..f04989d 100644 --- a/include/leveldb/db_path.h +++ b/include/leveldb/db_path.h @@ -17,28 +17,35 @@ public: virtual ~DbPath() {} - const std::string& Name() const { return path_; } - const char* CName() const { return path_.c_str(); } + const std::string& ToString() const { return path_; } + const char* ToCString() const { return path_.c_str(); } + + const char operator[](size_t i) const { return path_[i]; } + + static bool IsDirectorySeparator(const char c); virtual bool IsAbsolute() const = 0; virtual bool IsRelative() const = 0; - virtual size_t RootLength() = 0; + virtual size_t RootLength() const = 0; inline size_t Size() const { return path_.size(); } inline bool IsEmpty() const { return path_.empty(); } - + inline const std::string Substring(size_t from, size_t to) const { + return path_.substr(from, to); + } + inline const std::string GetRootDirectory() const { + return path_.substr(0, RootLength()); + } protected: DbPath() : path_("") {} DbPath(const std::string& path) : path_(path) {} - std::string path_; - - static bool IsDirectorySeparator(const char c); - - bool StartsWith(const std::string& value, bool ignore_case = false); - virtual void Normalize() = 0; + + bool StartsWith(const std::string& value, bool ignore_case = false) const; + + std::string path_; }; // Win path @@ -51,8 +58,7 @@ public: bool IsAbsolute() const override; bool IsRelative() const override; - - size_t RootLength() override; + size_t RootLength() const override; protected: static bool IsValidDriveChar(const char c); diff --git a/include/leveldb/env.h b/include/leveldb/env.h index e00895a..ef83ae2 100644 --- a/include/leveldb/env.h +++ b/include/leveldb/env.h @@ -20,6 +20,7 @@ #include "leveldb/export.h" #include "leveldb/status.h" +#include "leveldb/db_path.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 the input directory path exists. + virtual bool DirectoryExists(const std::string& dirname) = 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. @@ -141,6 +145,8 @@ class LEVELDB_EXPORT Env { // Create the specified directory. virtual Status CreateDir(const std::string& dirname) = 0; + virtual Status CreateDir(const path::DbPath& dbpath) = 0; + // Delete the specified directory. // // The default implementation calls DeleteDir, to support legacy Env @@ -358,6 +364,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 +377,9 @@ class LEVELDB_EXPORT EnvWrapper : public Env { Status CreateDir(const std::string& d) override { return target_->CreateDir(d); } + Status CreateDir(const path::DbPath& d) override { + return target_->CreateDir(d); + } Status RemoveDir(const std::string& d) override { return target_->RemoveDir(d); } diff --git a/util/db_path.cc b/util/db_path.cc index 9fe7e72..e3618e5 100644 --- a/util/db_path.cc +++ b/util/db_path.cc @@ -7,7 +7,7 @@ bool DbPath::IsDirectorySeparator(const char c) { return (c == DbPath::kDirectorySeparator || c == DbPath::kAltDirecttorySeparator); } -bool DbPath::StartsWith(const std::string& value, bool ignore_case) { +bool DbPath::StartsWith(const std::string& value, bool ignore_case) const { if (value.size() > path_.size()) { return false; } @@ -43,30 +43,12 @@ bool WindowsDbPath::IsRelative() const { return true; }; -void WindowsDbPath::Normalize() { - auto out = path_.begin(); - - for (const char c : path_) { - if (!IsDirectorySeparator(c)) { - *(out++) = c; - } - else if (out == path_.begin() || !IsDirectorySeparator(*std::prev(out))) { - *(out++) = kDirectorySeparator; - } - else { - continue; - } - } - - path_.erase(out, path_.end()); -} - bool WindowsDbPath::IsValidDriveChar(const char c) { const char drive_char = std::toupper(c); return drive_char >= 'A' && drive_char <= 'Z'; } -size_t WindowsDbPath::RootLength() { +size_t WindowsDbPath::RootLength() const { size_t path_length = path_.size(); size_t root_length = 0; size_t volume_separator_length = 2; @@ -78,8 +60,7 @@ size_t WindowsDbPath::RootLength() { if (extended_syntax) { if (extended_unc_syntax) { unc_root_length = std::strlen(kUncExtendedPathPrefix); - } - else { + } else { volume_separator_length += std::strlen(kExtendedPathPrefix); } } @@ -89,15 +70,17 @@ size_t WindowsDbPath::RootLength() { if (extended_unc_syntax || (path_length > 1 && IsDirectorySeparator(path_[1]))) { root_length = unc_root_length; - int n = 2; // maximum separators to skip - while (root_length < path_length && (!IsDirectorySeparator(path_[root_length]) || --n > 0)) { + int n = 2; // maximum separators to skip + while (root_length < path_length && + (!IsDirectorySeparator(path_[root_length]) || --n > 0)) { ++root_length; } } - } - else if (path_length >= volume_separator_length && path_[volume_separator_length - 1] == kVolumeSeparatorChar) { + } else if (path_length >= volume_separator_length && + path_[volume_separator_length - 1] == kVolumeSeparatorChar) { root_length = volume_separator_length; - if (path_length >= volume_separator_length && IsDirectorySeparator(path_[volume_separator_length])) { + if (path_length >= volume_separator_length && + IsDirectorySeparator(path_[volume_separator_length])) { ++root_length; } } @@ -105,6 +88,22 @@ size_t WindowsDbPath::RootLength() { return root_length; } +void WindowsDbPath::Normalize() { + auto out = path_.begin(); + + for (const char c : path_) { + if (!IsDirectorySeparator(c)) { + *(out++) = c; + } else if (out == path_.begin() || !IsDirectorySeparator(*std::prev(out))) { + *(out++) = kDirectorySeparator; + } else { + continue; + } + } + + path_.erase(out, path_.end()); +} + // Windows path DbPath* PathFactory::Create(const std::string& path) { diff --git a/util/env_windows.cc b/util/env_windows.cc index 1c74b02..f6751b2 100644 --- a/util/env_windows.cc +++ b/util/env_windows.cc @@ -37,6 +37,8 @@ namespace leveldb { namespace { +using namespace path; + constexpr const size_t kWritableFileBufferSize = 65536; // Up to 1000 mmaps for 64-bit binaries; none for 32-bit. @@ -492,6 +494,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 +542,54 @@ class WindowsEnv : public Env { return Status::OK(); } + Status CreateDir(const DbPath& dirpath) override { + size_t path_length = dirpath.Size(); + + if (path_length >= 2 && DbPath::IsDirectorySeparator(dirpath[path_length - 1])) { + --path_length; + } + + if (DirectoryExists(dirpath.ToString())) { + return Status::OK(); + } + + std::vector stackDir; + bool path_exists = false; + size_t root_length = dirpath.RootLength(); + + if (path_length > root_length) { // Special case root (fullpath = X:\\) + size_t i = path_length - 1; + + while (i >= root_length && !path_exists) { + const std::string dir = dirpath.Substring(0, i + 1); + + if (!DirectoryExists(dir)) { + stackDir.push_back(dir); + } + else { + path_exists = true; + } + + while (i > root_length && dirpath[i] != DbPath::kDirectorySeparator && dirpath[i] != DbPath::kAltDirecttorySeparator) + --i; + --i; + } + } + + while (!stackDir.empty()) { + const std::string dir = stackDir[stackDir.size() - 1]; + stackDir.pop_back(); + + if (!DirectoryExists(dir)) { + if (!::CreateDirectoryA(dir.c_str(), nullptr)) { + return WindowsError(dir, ::GetLastError()); + } + } + } + + return Status::OK(); + } + Status RemoveDir(const std::string& dirname) override { if (!::RemoveDirectoryA(dirname.c_str())) { return WindowsError(dirname, ::GetLastError());