Improvements to Env::GetChildren (#7819)
Summary: The main improvement here is to not include `.` or `..` in the results of `Env::GetChildren`. The occurrence of `.` or `..`; it is non-portable, dependent on the Operating System and the File System. See: https://www.gnu.org/software/libc/manual/html_node/Reading_002fClosing-Directory.html There were lots of duplicate checks spread through the RocksDB codebase previously to skip `.` and `..`. This new removes the need for those at the source. Also some minor fixes to `Env::GetChildren`: * Improve error handling in POSIX implementation * Remove unnecessary array allocation on Windows * Fix struct name for Windows Non-UTF-8 API Pull Request resolved: https://github.com/facebook/rocksdb/pull/7819 Reviewed By: ajkr Differential Revision: D25837394 Pulled By: jay-zhuang fbshipit-source-id: 1e137e7218d38b450af9c083f73d5357abcbba2e
This commit is contained in:
parent
8ed680bdb0
commit
4926b33742
@ -30,6 +30,7 @@
|
|||||||
|
|
||||||
### Public API Change
|
### Public API Change
|
||||||
* Deprecated public but rarely-used FilterBitsBuilder::CalculateNumEntry, which is replaced with ApproximateNumEntries taking a size_t parameter and returning size_t.
|
* Deprecated public but rarely-used FilterBitsBuilder::CalculateNumEntry, which is replaced with ApproximateNumEntries taking a size_t parameter and returning size_t.
|
||||||
|
* To improve portability the functions `Env::GetChildren` and `Env::GetChildrenFileAttributes` will no longer return entries for the special directories `.` or `..`.
|
||||||
|
|
||||||
## 6.15.0 (11/13/2020)
|
## 6.15.0 (11/13/2020)
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
@ -898,9 +898,7 @@ TEST_P(ColumnFamilyTest, IgnoreRecoveredLog) {
|
|||||||
std::vector<std::string> old_files;
|
std::vector<std::string> old_files;
|
||||||
ASSERT_OK(env_->GetChildren(backup_logs, &old_files));
|
ASSERT_OK(env_->GetChildren(backup_logs, &old_files));
|
||||||
for (auto& file : old_files) {
|
for (auto& file : old_files) {
|
||||||
if (file != "." && file != "..") {
|
ASSERT_OK(env_->DeleteFile(backup_logs + "/" + file));
|
||||||
ASSERT_OK(env_->DeleteFile(backup_logs + "/" + file));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
column_family_options_.merge_operator =
|
column_family_options_.merge_operator =
|
||||||
@ -929,9 +927,7 @@ TEST_P(ColumnFamilyTest, IgnoreRecoveredLog) {
|
|||||||
std::vector<std::string> logs;
|
std::vector<std::string> logs;
|
||||||
ASSERT_OK(env_->GetChildren(db_options_.wal_dir, &logs));
|
ASSERT_OK(env_->GetChildren(db_options_.wal_dir, &logs));
|
||||||
for (auto& log : logs) {
|
for (auto& log : logs) {
|
||||||
if (log != ".." && log != ".") {
|
CopyFile(db_options_.wal_dir + "/" + log, backup_logs + "/" + log);
|
||||||
CopyFile(db_options_.wal_dir + "/" + log, backup_logs + "/" + log);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// recover the DB
|
// recover the DB
|
||||||
@ -956,9 +952,7 @@ TEST_P(ColumnFamilyTest, IgnoreRecoveredLog) {
|
|||||||
if (iter == 0) {
|
if (iter == 0) {
|
||||||
// copy the logs from backup back to wal dir
|
// copy the logs from backup back to wal dir
|
||||||
for (auto& log : logs) {
|
for (auto& log : logs) {
|
||||||
if (log != ".." && log != ".") {
|
CopyFile(backup_logs + "/" + log, db_options_.wal_dir + "/" + log);
|
||||||
CopyFile(backup_logs + "/" + log, db_options_.wal_dir + "/" + log);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,7 @@ TEST_F(DBEncryptionTest, CheckEncrypted) {
|
|||||||
Env* target = GetTargetEnv();
|
Env* target = GetTargetEnv();
|
||||||
int hits = 0;
|
int hits = 0;
|
||||||
for (auto it = fileNames.begin() ; it != fileNames.end(); ++it) {
|
for (auto it = fileNames.begin() ; it != fileNames.end(); ++it) {
|
||||||
if ((*it == "..") || (*it == ".") || (*it == "LOCK")) {
|
if (*it == "LOCK") {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
auto filePath = dbname_ + "/" + *it;
|
auto filePath = dbname_ + "/" + *it;
|
||||||
|
@ -41,9 +41,7 @@ TEST_F(DBTest2, OpenForReadOnly) {
|
|||||||
std::vector<std::string> files;
|
std::vector<std::string> files;
|
||||||
ASSERT_OK(env_->GetChildren(dbname, &files));
|
ASSERT_OK(env_->GetChildren(dbname, &files));
|
||||||
for (auto& f : files) {
|
for (auto& f : files) {
|
||||||
if (f != "." && f != "..") {
|
ASSERT_OK(env_->DeleteFile(dbname + "/" + f));
|
||||||
ASSERT_OK(env_->DeleteFile(dbname + "/" + f));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// <dbname> should be empty now and we should be able to delete it
|
// <dbname> should be empty now and we should be able to delete it
|
||||||
ASSERT_OK(env_->DeleteDir(dbname));
|
ASSERT_OK(env_->DeleteDir(dbname));
|
||||||
@ -75,9 +73,7 @@ TEST_F(DBTest2, OpenForReadOnlyWithColumnFamilies) {
|
|||||||
std::vector<std::string> files;
|
std::vector<std::string> files;
|
||||||
ASSERT_OK(env_->GetChildren(dbname, &files));
|
ASSERT_OK(env_->GetChildren(dbname, &files));
|
||||||
for (auto& f : files) {
|
for (auto& f : files) {
|
||||||
if (f != "." && f != "..") {
|
ASSERT_OK(env_->DeleteFile(dbname + "/" + f));
|
||||||
ASSERT_OK(env_->DeleteFile(dbname + "/" + f));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// <dbname> should be empty now and we should be able to delete it
|
// <dbname> should be empty now and we should be able to delete it
|
||||||
ASSERT_OK(env_->DeleteDir(dbname));
|
ASSERT_OK(env_->DeleteDir(dbname));
|
||||||
|
@ -506,9 +506,7 @@ TEST_F(DBWALTest, IgnoreRecoveredLog) {
|
|||||||
std::vector<std::string> old_files;
|
std::vector<std::string> old_files;
|
||||||
ASSERT_OK(env_->GetChildren(backup_logs, &old_files));
|
ASSERT_OK(env_->GetChildren(backup_logs, &old_files));
|
||||||
for (auto& file : old_files) {
|
for (auto& file : old_files) {
|
||||||
if (file != "." && file != "..") {
|
ASSERT_OK(env_->DeleteFile(backup_logs + "/" + file));
|
||||||
ASSERT_OK(env_->DeleteFile(backup_logs + "/" + file));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Options options = CurrentOptions();
|
Options options = CurrentOptions();
|
||||||
options.create_if_missing = true;
|
options.create_if_missing = true;
|
||||||
@ -528,9 +526,7 @@ TEST_F(DBWALTest, IgnoreRecoveredLog) {
|
|||||||
std::vector<std::string> logs;
|
std::vector<std::string> logs;
|
||||||
ASSERT_OK(env_->GetChildren(options.wal_dir, &logs));
|
ASSERT_OK(env_->GetChildren(options.wal_dir, &logs));
|
||||||
for (auto& log : logs) {
|
for (auto& log : logs) {
|
||||||
if (log != ".." && log != ".") {
|
CopyFile(options.wal_dir + "/" + log, backup_logs + "/" + log);
|
||||||
CopyFile(options.wal_dir + "/" + log, backup_logs + "/" + log);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// recover the DB
|
// recover the DB
|
||||||
@ -541,9 +537,7 @@ TEST_F(DBWALTest, IgnoreRecoveredLog) {
|
|||||||
|
|
||||||
// copy the logs from backup back to wal dir
|
// copy the logs from backup back to wal dir
|
||||||
for (auto& log : logs) {
|
for (auto& log : logs) {
|
||||||
if (log != ".." && log != ".") {
|
CopyFile(backup_logs + "/" + log, options.wal_dir + "/" + log);
|
||||||
CopyFile(backup_logs + "/" + log, options.wal_dir + "/" + log);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// this should ignore the log files, recovery should not happen again
|
// this should ignore the log files, recovery should not happen again
|
||||||
// if the recovery happens, the same merge operator would be called twice,
|
// if the recovery happens, the same merge operator would be called twice,
|
||||||
@ -559,9 +553,7 @@ TEST_F(DBWALTest, IgnoreRecoveredLog) {
|
|||||||
// copy the logs from backup back to wal dir
|
// copy the logs from backup back to wal dir
|
||||||
ASSERT_OK(env_->CreateDirIfMissing(options.wal_dir));
|
ASSERT_OK(env_->CreateDirIfMissing(options.wal_dir));
|
||||||
for (auto& log : logs) {
|
for (auto& log : logs) {
|
||||||
if (log != ".." && log != ".") {
|
CopyFile(backup_logs + "/" + log, options.wal_dir + "/" + log);
|
||||||
CopyFile(backup_logs + "/" + log, options.wal_dir + "/" + log);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// assert that we successfully recovered only from logs, even though we
|
// assert that we successfully recovered only from logs, even though we
|
||||||
// destroyed the DB
|
// destroyed the DB
|
||||||
@ -574,11 +566,9 @@ TEST_F(DBWALTest, IgnoreRecoveredLog) {
|
|||||||
// copy the logs from backup back to wal dir
|
// copy the logs from backup back to wal dir
|
||||||
ASSERT_OK(env_->CreateDirIfMissing(options.wal_dir));
|
ASSERT_OK(env_->CreateDirIfMissing(options.wal_dir));
|
||||||
for (auto& log : logs) {
|
for (auto& log : logs) {
|
||||||
if (log != ".." && log != ".") {
|
CopyFile(backup_logs + "/" + log, options.wal_dir + "/" + log);
|
||||||
CopyFile(backup_logs + "/" + log, options.wal_dir + "/" + log);
|
// we won't be needing this file no more
|
||||||
// we won't be needing this file no more
|
ASSERT_OK(env_->DeleteFile(backup_logs + "/" + log));
|
||||||
ASSERT_OK(env_->DeleteFile(backup_logs + "/" + log));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Status s = TryReopen(options);
|
Status s = TryReopen(options);
|
||||||
ASSERT_NOK(s);
|
ASSERT_NOK(s);
|
||||||
|
93
env/env_basic_test.cc
vendored
93
env/env_basic_test.cc
vendored
@ -10,6 +10,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "env/mock_env.h"
|
#include "env/mock_env.h"
|
||||||
|
#include "file/file_util.h"
|
||||||
#include "rocksdb/convenience.h"
|
#include "rocksdb/convenience.h"
|
||||||
#include "rocksdb/env.h"
|
#include "rocksdb/env.h"
|
||||||
#include "rocksdb/env_encryption.h"
|
#include "rocksdb/env_encryption.h"
|
||||||
@ -17,46 +18,6 @@
|
|||||||
|
|
||||||
namespace ROCKSDB_NAMESPACE {
|
namespace ROCKSDB_NAMESPACE {
|
||||||
|
|
||||||
// Normalizes trivial differences across Envs such that these test cases can
|
|
||||||
// run on all Envs.
|
|
||||||
class NormalizingEnvWrapper : public EnvWrapper {
|
|
||||||
private:
|
|
||||||
std::unique_ptr<Env> base_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit NormalizingEnvWrapper(std::unique_ptr<Env>&& base)
|
|
||||||
: EnvWrapper(base.get()), base_(std::move(base)) {}
|
|
||||||
explicit NormalizingEnvWrapper(Env* base) : EnvWrapper(base) {}
|
|
||||||
|
|
||||||
// Removes . and .. from directory listing
|
|
||||||
Status GetChildren(const std::string& dir,
|
|
||||||
std::vector<std::string>* result) override {
|
|
||||||
Status status = EnvWrapper::GetChildren(dir, result);
|
|
||||||
if (status.ok()) {
|
|
||||||
result->erase(std::remove_if(result->begin(), result->end(),
|
|
||||||
[](const std::string& s) {
|
|
||||||
return s == "." || s == "..";
|
|
||||||
}),
|
|
||||||
result->end());
|
|
||||||
}
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Removes . and .. from directory listing
|
|
||||||
Status GetChildrenFileAttributes(
|
|
||||||
const std::string& dir, std::vector<FileAttributes>* result) override {
|
|
||||||
Status status = EnvWrapper::GetChildrenFileAttributes(dir, result);
|
|
||||||
if (status.ok()) {
|
|
||||||
result->erase(std::remove_if(result->begin(), result->end(),
|
|
||||||
[](const FileAttributes& fa) {
|
|
||||||
return fa.name == "." || fa.name == "..";
|
|
||||||
}),
|
|
||||||
result->end());
|
|
||||||
}
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class EnvBasicTestWithParam : public testing::Test,
|
class EnvBasicTestWithParam : public testing::Test,
|
||||||
public ::testing::WithParamInterface<Env*> {
|
public ::testing::WithParamInterface<Env*> {
|
||||||
public:
|
public:
|
||||||
@ -68,32 +29,17 @@ class EnvBasicTestWithParam : public testing::Test,
|
|||||||
test_dir_ = test::PerThreadDBPath(env_, "env_basic_test");
|
test_dir_ = test::PerThreadDBPath(env_, "env_basic_test");
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetUp() override {
|
void SetUp() override { ASSERT_OK(env_->CreateDirIfMissing(test_dir_)); }
|
||||||
env_->CreateDirIfMissing(test_dir_).PermitUncheckedError();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TearDown() override {
|
void TearDown() override { ASSERT_OK(DestroyDir(env_, test_dir_)); }
|
||||||
std::vector<std::string> files;
|
|
||||||
env_->GetChildren(test_dir_, &files).PermitUncheckedError();
|
|
||||||
for (const auto& file : files) {
|
|
||||||
// don't know whether it's file or directory, try both. The tests must
|
|
||||||
// only create files or empty directories, so one must succeed, else the
|
|
||||||
// directory's corrupted.
|
|
||||||
Status s = env_->DeleteFile(test_dir_ + "/" + file);
|
|
||||||
if (!s.ok()) {
|
|
||||||
ASSERT_OK(env_->DeleteDir(test_dir_ + "/" + file));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class EnvMoreTestWithParam : public EnvBasicTestWithParam {};
|
class EnvMoreTestWithParam : public EnvBasicTestWithParam {};
|
||||||
|
|
||||||
static std::unique_ptr<Env> def_env(new NormalizingEnvWrapper(Env::Default()));
|
|
||||||
INSTANTIATE_TEST_CASE_P(EnvDefault, EnvBasicTestWithParam,
|
INSTANTIATE_TEST_CASE_P(EnvDefault, EnvBasicTestWithParam,
|
||||||
::testing::Values(def_env.get()));
|
::testing::Values(Env::Default()));
|
||||||
INSTANTIATE_TEST_CASE_P(EnvDefault, EnvMoreTestWithParam,
|
INSTANTIATE_TEST_CASE_P(EnvDefault, EnvMoreTestWithParam,
|
||||||
::testing::Values(def_env.get()));
|
::testing::Values(Env::Default()));
|
||||||
|
|
||||||
static std::unique_ptr<Env> mock_env(new MockEnv(Env::Default()));
|
static std::unique_ptr<Env> mock_env(new MockEnv(Env::Default()));
|
||||||
INSTANTIATE_TEST_CASE_P(MockEnv, EnvBasicTestWithParam,
|
INSTANTIATE_TEST_CASE_P(MockEnv, EnvBasicTestWithParam,
|
||||||
@ -104,8 +50,7 @@ static Env* NewTestEncryptedEnv(Env* base, const std::string& provider_id) {
|
|||||||
std::shared_ptr<EncryptionProvider> provider;
|
std::shared_ptr<EncryptionProvider> provider;
|
||||||
EXPECT_OK(EncryptionProvider::CreateFromString(ConfigOptions(), provider_id,
|
EXPECT_OK(EncryptionProvider::CreateFromString(ConfigOptions(), provider_id,
|
||||||
&provider));
|
&provider));
|
||||||
std::unique_ptr<Env> encrypted(NewEncryptedEnv(base, provider));
|
return NewEncryptedEnv(base, provider);
|
||||||
return new NormalizingEnvWrapper(std::move(encrypted));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// next statements run env test against default encryption code.
|
// next statements run env test against default encryption code.
|
||||||
@ -374,6 +319,32 @@ TEST_P(EnvMoreTestWithParam, GetChildren) {
|
|||||||
ASSERT_EQ(0U, children.size());
|
ASSERT_EQ(0U, children.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_P(EnvMoreTestWithParam, GetChildrenIgnoresDotAndDotDot) {
|
||||||
|
auto* env = Env::Default();
|
||||||
|
ASSERT_OK(env->CreateDirIfMissing(test_dir_));
|
||||||
|
|
||||||
|
// Create a single file
|
||||||
|
std::string path = test_dir_;
|
||||||
|
const EnvOptions soptions;
|
||||||
|
#ifdef OS_WIN
|
||||||
|
path.append("\\test_file");
|
||||||
|
#else
|
||||||
|
path.append("/test_file");
|
||||||
|
#endif
|
||||||
|
std::string data("test data");
|
||||||
|
std::unique_ptr<WritableFile> file;
|
||||||
|
ASSERT_OK(env->NewWritableFile(path, &file, soptions));
|
||||||
|
ASSERT_OK(file->Append("test data"));
|
||||||
|
|
||||||
|
// get the children
|
||||||
|
std::vector<std::string> result;
|
||||||
|
ASSERT_OK(env->GetChildren(test_dir_, &result));
|
||||||
|
|
||||||
|
// expect only one file named `test_data`, i.e. no `.` or `..` names
|
||||||
|
ASSERT_EQ(result.size(), 1);
|
||||||
|
ASSERT_EQ(result.at(0), "test_file");
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace ROCKSDB_NAMESPACE
|
} // namespace ROCKSDB_NAMESPACE
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
::testing::InitGoogleTest(&argc, argv);
|
::testing::InitGoogleTest(&argc, argv);
|
||||||
|
30
env/fs_posix.cc
vendored
30
env/fs_posix.cc
vendored
@ -607,6 +607,7 @@ class PosixFileSystem : public FileSystem {
|
|||||||
std::vector<std::string>* result,
|
std::vector<std::string>* result,
|
||||||
IODebugContext* /*dbg*/) override {
|
IODebugContext* /*dbg*/) override {
|
||||||
result->clear();
|
result->clear();
|
||||||
|
|
||||||
DIR* d = opendir(dir.c_str());
|
DIR* d = opendir(dir.c_str());
|
||||||
if (d == nullptr) {
|
if (d == nullptr) {
|
||||||
switch (errno) {
|
switch (errno) {
|
||||||
@ -618,11 +619,34 @@ class PosixFileSystem : public FileSystem {
|
|||||||
return IOError("While opendir", dir, errno);
|
return IOError("While opendir", dir, errno);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const auto pre_read_errno = errno; // errno may be modified by readdir
|
||||||
struct dirent* entry;
|
struct dirent* entry;
|
||||||
while ((entry = readdir(d)) != nullptr) {
|
while ((entry = readdir(d)) != nullptr && errno == pre_read_errno) {
|
||||||
result->push_back(entry->d_name);
|
// filter out '.' and '..' directory entries
|
||||||
|
// which appear only on some platforms
|
||||||
|
const bool ignore =
|
||||||
|
entry->d_type == DT_DIR &&
|
||||||
|
(strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0);
|
||||||
|
if (!ignore) {
|
||||||
|
result->push_back(entry->d_name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
closedir(d);
|
|
||||||
|
// always attempt to close the dir
|
||||||
|
const auto pre_close_errno = errno; // errno may be modified by closedir
|
||||||
|
const int close_result = closedir(d);
|
||||||
|
|
||||||
|
if (pre_close_errno != pre_read_errno) {
|
||||||
|
// error occured during readdir
|
||||||
|
return IOError("While readdir", dir, pre_close_errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (close_result != 0) {
|
||||||
|
// error occured during closedir
|
||||||
|
return IOError("While closedir", dir, errno);
|
||||||
|
}
|
||||||
|
|
||||||
return IOStatus::OK();
|
return IOStatus::OK();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ class DeleteSchedulerTest : public testing::Test {
|
|||||||
|
|
||||||
int normal_cnt = 0;
|
int normal_cnt = 0;
|
||||||
for (auto& f : files_in_dir) {
|
for (auto& f : files_in_dir) {
|
||||||
if (!DeleteScheduler::IsTrashFile(f) && f != "." && f != "..") {
|
if (!DeleteScheduler::IsTrashFile(f)) {
|
||||||
normal_cnt++;
|
normal_cnt++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -230,9 +230,6 @@ Status DestroyDir(Env* env, const std::string& dir) {
|
|||||||
s = env->GetChildren(dir, &files_in_dir);
|
s = env->GetChildren(dir, &files_in_dir);
|
||||||
if (s.ok()) {
|
if (s.ok()) {
|
||||||
for (auto& file_in_dir : files_in_dir) {
|
for (auto& file_in_dir : files_in_dir) {
|
||||||
if (file_in_dir == "." || file_in_dir == "..") {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
std::string path = dir + "/" + file_in_dir;
|
std::string path = dir + "/" + file_in_dir;
|
||||||
bool is_dir = false;
|
bool is_dir = false;
|
||||||
s = env->IsDirectory(path, &is_dir);
|
s = env->IsDirectory(path, &is_dir);
|
||||||
|
@ -510,10 +510,6 @@ SstFileManager* NewSstFileManager(Env* env, std::shared_ptr<FileSystem> fs,
|
|||||||
s = fs->GetChildren(trash_dir, IOOptions(), &files_in_trash, nullptr);
|
s = fs->GetChildren(trash_dir, IOOptions(), &files_in_trash, nullptr);
|
||||||
if (s.ok()) {
|
if (s.ok()) {
|
||||||
for (const std::string& trash_file : files_in_trash) {
|
for (const std::string& trash_file : files_in_trash) {
|
||||||
if (trash_file == "." || trash_file == "..") {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string path_in_trash = trash_dir + "/" + trash_file;
|
std::string path_in_trash = trash_dir + "/" + trash_file;
|
||||||
res->OnAddFile(path_in_trash);
|
res->OnAddFile(path_in_trash);
|
||||||
Status file_delete =
|
Status file_delete =
|
||||||
|
@ -283,7 +283,8 @@ class Env {
|
|||||||
virtual Status FileExists(const std::string& fname) = 0;
|
virtual Status FileExists(const std::string& fname) = 0;
|
||||||
|
|
||||||
// Store in *result the names of the children of the specified directory.
|
// Store in *result the names of the children of the specified directory.
|
||||||
// The names are relative to "dir".
|
// The names are relative to "dir", and shall never include the
|
||||||
|
// names `.` or `..`.
|
||||||
// Original contents of *results are dropped.
|
// Original contents of *results are dropped.
|
||||||
// Returns OK if "dir" exists and "*result" contains its children.
|
// Returns OK if "dir" exists and "*result" contains its children.
|
||||||
// NotFound if "dir" does not exist, the calling process does not have
|
// NotFound if "dir" does not exist, the calling process does not have
|
||||||
@ -296,7 +297,8 @@ class Env {
|
|||||||
// In case the implementation lists the directory prior to iterating the files
|
// In case the implementation lists the directory prior to iterating the files
|
||||||
// and files are concurrently deleted, the deleted files will be omitted from
|
// and files are concurrently deleted, the deleted files will be omitted from
|
||||||
// result.
|
// result.
|
||||||
// The name attributes are relative to "dir".
|
// The name attributes are relative to "dir", and shall never include the
|
||||||
|
// names `.` or `..`.
|
||||||
// Original contents of *results are dropped.
|
// Original contents of *results are dropped.
|
||||||
// Returns OK if "dir" exists and "*result" contains its children.
|
// Returns OK if "dir" exists and "*result" contains its children.
|
||||||
// NotFound if "dir" does not exist, the calling process does not have
|
// NotFound if "dir" does not exist, the calling process does not have
|
||||||
|
@ -645,7 +645,6 @@ IOStatus WinFileSystem::GetChildren(const std::string& dir,
|
|||||||
IODebugContext* /*dbg*/) {
|
IODebugContext* /*dbg*/) {
|
||||||
IOStatus status;
|
IOStatus status;
|
||||||
result->clear();
|
result->clear();
|
||||||
std::vector<std::string> output;
|
|
||||||
|
|
||||||
RX_WIN32_FIND_DATA data;
|
RX_WIN32_FIND_DATA data;
|
||||||
memset(&data, 0, sizeof(data));
|
memset(&data, 0, sizeof(data));
|
||||||
@ -677,16 +676,20 @@ IOStatus WinFileSystem::GetChildren(const std::string& dir,
|
|||||||
|
|
||||||
UniqueFindClosePtr fc(handle, FindCloseFunc);
|
UniqueFindClosePtr fc(handle, FindCloseFunc);
|
||||||
|
|
||||||
if (result->capacity() > 0) {
|
|
||||||
output.reserve(result->capacity());
|
|
||||||
}
|
|
||||||
|
|
||||||
// For safety
|
// For safety
|
||||||
data.cFileName[MAX_PATH - 1] = 0;
|
data.cFileName[MAX_PATH - 1] = 0;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
auto x = RX_FILESTRING(data.cFileName, RX_FNLEN(data.cFileName));
|
// filter out '.' and '..' directory entries
|
||||||
output.emplace_back(FN_TO_RX(x));
|
// which appear only on some platforms
|
||||||
|
const bool ignore =
|
||||||
|
((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) &&
|
||||||
|
(strcmp(data.cFileName, ".") == 0 || strcmp(data.cFileName, "..") == 0);
|
||||||
|
if (!ignore) {
|
||||||
|
auto x = RX_FILESTRING(data.cFileName, RX_FNLEN(data.cFileName));
|
||||||
|
result->push_back(FN_TO_RX(x));
|
||||||
|
}
|
||||||
|
|
||||||
BOOL ret = -RX_FindNextFile(handle, &data);
|
BOOL ret = -RX_FindNextFile(handle, &data);
|
||||||
// If the function fails the return value is zero
|
// If the function fails the return value is zero
|
||||||
// and non-zero otherwise. Not TRUE or FALSE.
|
// and non-zero otherwise. Not TRUE or FALSE.
|
||||||
@ -696,7 +699,6 @@ IOStatus WinFileSystem::GetChildren(const std::string& dir,
|
|||||||
}
|
}
|
||||||
data.cFileName[MAX_PATH - 1] = 0;
|
data.cFileName[MAX_PATH - 1] = 0;
|
||||||
}
|
}
|
||||||
output.swap(*result);
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -388,7 +388,7 @@ extern void SetCpuPriority(ThreadId id, CpuPriority priority);
|
|||||||
#define RX_FindFirstFileEx FindFirstFileExA
|
#define RX_FindFirstFileEx FindFirstFileExA
|
||||||
#define RX_CreateDirectory CreateDirectoryA
|
#define RX_CreateDirectory CreateDirectoryA
|
||||||
#define RX_FindNextFile FindNextFileA
|
#define RX_FindNextFile FindNextFileA
|
||||||
#define RX_WIN32_FIND_DATA WIN32_FIND_DATA
|
#define RX_WIN32_FIND_DATA WIN32_FIND_DATAA
|
||||||
#define RX_CreateDirectory CreateDirectoryA
|
#define RX_CreateDirectory CreateDirectoryA
|
||||||
#define RX_RemoveDirectory RemoveDirectoryA
|
#define RX_RemoveDirectory RemoveDirectoryA
|
||||||
#define RX_GetFileAttributesEx GetFileAttributesExA
|
#define RX_GetFileAttributesEx GetFileAttributesExA
|
||||||
|
@ -743,9 +743,6 @@ Status BackupEngineImpl::Initialize() {
|
|||||||
}
|
}
|
||||||
// create backups_ structure
|
// create backups_ structure
|
||||||
for (auto& file : backup_meta_files) {
|
for (auto& file : backup_meta_files) {
|
||||||
if (file == "." || file == "..") {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
ROCKS_LOG_INFO(options_.info_log, "Detected backup %s", file.c_str());
|
ROCKS_LOG_INFO(options_.info_log, "Detected backup %s", file.c_str());
|
||||||
BackupID backup_id = 0;
|
BackupID backup_id = 0;
|
||||||
sscanf(file.c_str(), "%u", &backup_id);
|
sscanf(file.c_str(), "%u", &backup_id);
|
||||||
@ -1985,9 +1982,6 @@ Status BackupEngineImpl::GarbageCollect() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (auto& child : shared_children) {
|
for (auto& child : shared_children) {
|
||||||
if (child == "." || child == "..") {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
std::string rel_fname;
|
std::string rel_fname;
|
||||||
if (with_checksum) {
|
if (with_checksum) {
|
||||||
rel_fname = GetSharedFileWithChecksumRel(child);
|
rel_fname = GetSharedFileWithChecksumRel(child);
|
||||||
@ -2024,10 +2018,6 @@ Status BackupEngineImpl::GarbageCollect() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (auto& child : private_children) {
|
for (auto& child : private_children) {
|
||||||
if (child == "." || child == "..") {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
BackupID backup_id = 0;
|
BackupID backup_id = 0;
|
||||||
bool tmp_dir = child.find(".tmp") != std::string::npos;
|
bool tmp_dir = child.find(".tmp") != std::string::npos;
|
||||||
sscanf(child.c_str(), "%u", &backup_id);
|
sscanf(child.c_str(), "%u", &backup_id);
|
||||||
@ -2042,9 +2032,6 @@ Status BackupEngineImpl::GarbageCollect() {
|
|||||||
std::vector<std::string> subchildren;
|
std::vector<std::string> subchildren;
|
||||||
if (backup_env_->GetChildren(full_private_path, &subchildren).ok()) {
|
if (backup_env_->GetChildren(full_private_path, &subchildren).ok()) {
|
||||||
for (auto& subchild : subchildren) {
|
for (auto& subchild : subchildren) {
|
||||||
if (subchild == "." || subchild == "..") {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Status s = backup_env_->DeleteFile(full_private_path + subchild);
|
Status s = backup_env_->DeleteFile(full_private_path + subchild);
|
||||||
ROCKS_LOG_INFO(options_.info_log, "Deleting %s -- %s",
|
ROCKS_LOG_INFO(options_.info_log, "Deleting %s -- %s",
|
||||||
(full_private_path + subchild).c_str(),
|
(full_private_path + subchild).c_str(),
|
||||||
|
@ -417,11 +417,9 @@ class FileManager : public EnvWrapper {
|
|||||||
assert(fname != nullptr);
|
assert(fname != nullptr);
|
||||||
while (true) {
|
while (true) {
|
||||||
int i = rnd_.Next() % children.size();
|
int i = rnd_.Next() % children.size();
|
||||||
if (children[i].name != "." && children[i].name != "..") {
|
fname->assign(dir + "/" + children[i].name);
|
||||||
fname->assign(dir + "/" + children[i].name);
|
*fsize = children[i].size_bytes;
|
||||||
*fsize = children[i].size_bytes;
|
return Status::OK();
|
||||||
return Status::OK();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// should never get here
|
// should never get here
|
||||||
assert(false);
|
assert(false);
|
||||||
@ -433,14 +431,10 @@ class FileManager : public EnvWrapper {
|
|||||||
Status s = GetChildren(dir, &children);
|
Status s = GetChildren(dir, &children);
|
||||||
if (!s.ok()) {
|
if (!s.ok()) {
|
||||||
return s;
|
return s;
|
||||||
} else if (children.size() <= 2) { // . and ..
|
|
||||||
return Status::NotFound("");
|
|
||||||
}
|
}
|
||||||
while (true) {
|
while (true) {
|
||||||
int i = rnd_.Next() % children.size();
|
int i = rnd_.Next() % children.size();
|
||||||
if (children[i] != "." && children[i] != "..") {
|
return DeleteFile(dir + "/" + children[i]);
|
||||||
return DeleteFile(dir + "/" + children[i]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// should never get here
|
// should never get here
|
||||||
assert(false);
|
assert(false);
|
||||||
@ -453,14 +447,10 @@ class FileManager : public EnvWrapper {
|
|||||||
Status s = GetChildren(dir, &children);
|
Status s = GetChildren(dir, &children);
|
||||||
if (!s.ok()) {
|
if (!s.ok()) {
|
||||||
return s;
|
return s;
|
||||||
} else if (children.size() <= 2) {
|
|
||||||
return Status::NotFound("");
|
|
||||||
}
|
}
|
||||||
while (true) {
|
while (true) {
|
||||||
int i = rnd_.Next() % children.size();
|
int i = rnd_.Next() % children.size();
|
||||||
if (children[i] != "." && children[i] != "..") {
|
return WriteToFile(dir + "/" + children[i], data);
|
||||||
return WriteToFile(dir + "/" + children[i], data);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// should never get here
|
// should never get here
|
||||||
assert(false);
|
assert(false);
|
||||||
@ -828,9 +818,6 @@ class BackupableDBTest : public testing::Test {
|
|||||||
ASSERT_OK(file_manager_->GetChildrenFileAttributes(dir, &children));
|
ASSERT_OK(file_manager_->GetChildrenFileAttributes(dir, &children));
|
||||||
int found_count = 0;
|
int found_count = 0;
|
||||||
for (const auto& child : children) {
|
for (const auto& child : children) {
|
||||||
if (child.name == "." || child.name == "..") {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const std::string match("match");
|
const std::string match("match");
|
||||||
ASSERT_EQ(match, std::regex_replace(child.name, pattern, match));
|
ASSERT_EQ(match, std::regex_replace(child.name, pattern, match));
|
||||||
++found_count;
|
++found_count;
|
||||||
@ -844,9 +831,6 @@ class BackupableDBTest : public testing::Test {
|
|||||||
ASSERT_OK(file_manager_->GetChildrenFileAttributes(dir, &children));
|
ASSERT_OK(file_manager_->GetChildrenFileAttributes(dir, &children));
|
||||||
int found_count = 0;
|
int found_count = 0;
|
||||||
for (const auto& child : children) {
|
for (const auto& child : children) {
|
||||||
if (child.name == "." || child.name == "..") {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
auto last_underscore = child.name.find_last_of('_');
|
auto last_underscore = child.name.find_last_of('_');
|
||||||
auto last_dot = child.name.find_last_of('.');
|
auto last_dot = child.name.find_last_of('.');
|
||||||
ASSERT_NE(child.name, child.name.substr(0, last_underscore));
|
ASSERT_NE(child.name, child.name.substr(0, last_underscore));
|
||||||
@ -1351,7 +1335,7 @@ TEST_F(BackupableDBTest, CorruptFileMaintainSize) {
|
|||||||
const std::string dir = backupdir_ + "/shared_checksum";
|
const std::string dir = backupdir_ + "/shared_checksum";
|
||||||
ASSERT_OK(file_manager_->GetChildrenFileAttributes(dir, &children));
|
ASSERT_OK(file_manager_->GetChildrenFileAttributes(dir, &children));
|
||||||
for (const auto& child : children) {
|
for (const auto& child : children) {
|
||||||
if (child.name == "." || child.name == ".." || child.size_bytes == 0) {
|
if (child.size_bytes == 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// corrupt the file by replacing its content by file_size random bytes
|
// corrupt the file by replacing its content by file_size random bytes
|
||||||
@ -1575,7 +1559,7 @@ TEST_F(BackupableDBTest, FlushCompactDuringBackupCheckpoint) {
|
|||||||
const std::string dir = backupdir_ + "/shared_checksum";
|
const std::string dir = backupdir_ + "/shared_checksum";
|
||||||
ASSERT_OK(file_manager_->GetChildrenFileAttributes(dir, &children));
|
ASSERT_OK(file_manager_->GetChildrenFileAttributes(dir, &children));
|
||||||
for (const auto& child : children) {
|
for (const auto& child : children) {
|
||||||
if (child.name == "." || child.name == ".." || child.size_bytes == 0) {
|
if (child.size_bytes == 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const std::string match("match");
|
const std::string match("match");
|
||||||
@ -2061,7 +2045,7 @@ TEST_F(BackupableDBTest, FileSizeForIncremental) {
|
|||||||
|
|
||||||
// Corrupt backup SST
|
// Corrupt backup SST
|
||||||
ASSERT_OK(file_manager_->GetChildrenFileAttributes(shared_dir, &children));
|
ASSERT_OK(file_manager_->GetChildrenFileAttributes(shared_dir, &children));
|
||||||
ASSERT_EQ(children.size(), 3U); // ".", "..", one sst
|
ASSERT_EQ(children.size(), 1U); // one sst
|
||||||
for (const auto& child : children) {
|
for (const auto& child : children) {
|
||||||
if (child.name.size() > 4 && child.size_bytes > 0) {
|
if (child.name.size() > 4 && child.size_bytes > 0) {
|
||||||
ASSERT_OK(
|
ASSERT_OK(
|
||||||
@ -2121,7 +2105,7 @@ TEST_F(BackupableDBTest, FileSizeForIncremental) {
|
|||||||
// Count backup SSTs
|
// Count backup SSTs
|
||||||
children.clear();
|
children.clear();
|
||||||
ASSERT_OK(file_manager_->GetChildrenFileAttributes(shared_dir, &children));
|
ASSERT_OK(file_manager_->GetChildrenFileAttributes(shared_dir, &children));
|
||||||
ASSERT_EQ(children.size(), 4U); // ".", "..", two sst
|
ASSERT_EQ(children.size(), 2U); // two sst
|
||||||
|
|
||||||
// Try create backup 3
|
// Try create backup 3
|
||||||
s = backup_engine_->CreateNewBackup(db_.get(), true /*flush*/);
|
s = backup_engine_->CreateNewBackup(db_.get(), true /*flush*/);
|
||||||
@ -2134,18 +2118,18 @@ TEST_F(BackupableDBTest, FileSizeForIncremental) {
|
|||||||
// Acceptable to call it corruption if size is not in name and
|
// Acceptable to call it corruption if size is not in name and
|
||||||
// db session id collision is practically impossible.
|
// db session id collision is practically impossible.
|
||||||
EXPECT_TRUE(s.IsCorruption());
|
EXPECT_TRUE(s.IsCorruption());
|
||||||
EXPECT_EQ(children.size(), 4U); // no SST added
|
EXPECT_EQ(children.size(), 2U); // no SST added
|
||||||
} else if (option == share_no_checksum) {
|
} else if (option == share_no_checksum) {
|
||||||
// Good to call it corruption if both backups cannot be
|
// Good to call it corruption if both backups cannot be
|
||||||
// accommodated.
|
// accommodated.
|
||||||
EXPECT_TRUE(s.IsCorruption());
|
EXPECT_TRUE(s.IsCorruption());
|
||||||
EXPECT_EQ(children.size(), 4U); // no SST added
|
EXPECT_EQ(children.size(), 2U); // no SST added
|
||||||
} else {
|
} else {
|
||||||
// Since opening a DB seems sufficient for detecting size corruption
|
// Since opening a DB seems sufficient for detecting size corruption
|
||||||
// on the DB side, this should be a good thing, ...
|
// on the DB side, this should be a good thing, ...
|
||||||
EXPECT_OK(s);
|
EXPECT_OK(s);
|
||||||
// ... as long as we did actually treat it as a distinct SST file.
|
// ... as long as we did actually treat it as a distinct SST file.
|
||||||
EXPECT_EQ(children.size(), 5U); // Another SST added
|
EXPECT_EQ(children.size(), 3U); // Another SST added
|
||||||
}
|
}
|
||||||
CloseDBAndBackupEngine();
|
CloseDBAndBackupEngine();
|
||||||
ASSERT_OK(DestroyDB(dbname_, options_));
|
ASSERT_OK(DestroyDB(dbname_, options_));
|
||||||
|
@ -325,13 +325,7 @@ TEST_F(CheckpointTest, ExportColumnFamilyWithLinks) {
|
|||||||
ASSERT_EQ(metadata.files.size(), num_files_expected);
|
ASSERT_EQ(metadata.files.size(), num_files_expected);
|
||||||
std::vector<std::string> subchildren;
|
std::vector<std::string> subchildren;
|
||||||
ASSERT_OK(env_->GetChildren(export_path_, &subchildren));
|
ASSERT_OK(env_->GetChildren(export_path_, &subchildren));
|
||||||
int num_children = 0;
|
ASSERT_EQ(subchildren.size(), num_files_expected);
|
||||||
for (const auto& child : subchildren) {
|
|
||||||
if (child != "." && child != "..") {
|
|
||||||
++num_children;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ASSERT_EQ(num_children, num_files_expected);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Test DefaultColumnFamily
|
// Test DefaultColumnFamily
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
|
#include "file/file_util.h"
|
||||||
#include "utilities/persistent_cache/block_cache_tier.h"
|
#include "utilities/persistent_cache/block_cache_tier.h"
|
||||||
|
|
||||||
namespace ROCKSDB_NAMESPACE {
|
namespace ROCKSDB_NAMESPACE {
|
||||||
@ -38,30 +39,9 @@ static void OnOpenForWrite(void* arg) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void RemoveDirectory(const std::string& folder) {
|
|
||||||
std::vector<std::string> files;
|
|
||||||
Status status = Env::Default()->GetChildren(folder, &files);
|
|
||||||
if (!status.ok()) {
|
|
||||||
// we assume the directory does not exist
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// cleanup files with the patter :digi:.rc
|
|
||||||
for (auto file : files) {
|
|
||||||
if (file == "." || file == "..") {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
status = Env::Default()->DeleteFile(folder + "/" + file);
|
|
||||||
assert(status.ok());
|
|
||||||
}
|
|
||||||
|
|
||||||
status = Env::Default()->DeleteDir(folder);
|
|
||||||
assert(status.ok());
|
|
||||||
}
|
|
||||||
|
|
||||||
static void OnDeleteDir(void* arg) {
|
static void OnDeleteDir(void* arg) {
|
||||||
char* dir = static_cast<char*>(arg);
|
char* dir = static_cast<char*>(arg);
|
||||||
RemoveDirectory(std::string(dir));
|
ASSERT_OK(DestroyDir(Env::Default(), std::string(dir)));
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
Loading…
Reference in New Issue
Block a user