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
main
Adam Retter 4 years ago committed by Facebook GitHub Bot
parent 8ed680bdb0
commit 4926b33742
  1. 1
      HISTORY.md
  2. 12
      db/column_family_test.cc
  3. 2
      db/db_encryption_test.cc
  4. 8
      db/db_test2.cc
  5. 24
      db/db_wal_test.cc
  6. 93
      env/env_basic_test.cc
  7. 30
      env/fs_posix.cc
  8. 2
      file/delete_scheduler_test.cc
  9. 3
      file/file_util.cc
  10. 4
      file/sst_file_manager_impl.cc
  11. 6
      include/rocksdb/env.h
  12. 18
      port/win/env_win.cc
  13. 2
      port/win/port_win.h
  14. 13
      utilities/backupable/backupable_db.cc
  15. 40
      utilities/backupable/backupable_db_test.cc
  16. 8
      utilities/checkpoint/checkpoint_test.cc
  17. 24
      utilities/persistent_cache/persistent_cache_test.cc

@ -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);

@ -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

@ -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…
Cancel
Save