Fix walk_path.

GitOrigin-RevId: 8461f7331f565dd257794f91953e500d7f1986bc
This commit is contained in:
levlam 2018-09-12 03:21:23 +03:00
parent 31333b5b7f
commit 1832683ec0
5 changed files with 68 additions and 56 deletions

View File

@ -223,13 +223,15 @@ class CreateFileBench : public Benchmark {
} }
} }
void tear_down() override { void tear_down() override {
auto status = td::walk_path("A/", [&](CSlice path, bool is_dir) { td::walk_path("A/",
[&](CSlice path, bool is_dir) {
if (is_dir) { if (is_dir) {
rmdir(path).ignore(); rmdir(path).ignore();
} else { } else {
unlink(path).ignore(); unlink(path).ignore();
} }
}); })
.ignore();
} }
}; };
@ -245,19 +247,23 @@ class WalkPathBench : public Benchmark {
} }
void run(int n) override { void run(int n) override {
int cnt = 0; int cnt = 0;
auto status = td::walk_path("A/", [&](CSlice path, bool is_dir) { td::walk_path("A/",
[&](CSlice path, bool is_dir) {
stat(path).ok(); stat(path).ok();
cnt++; cnt++;
}); })
.ignore();
} }
void tear_down() override { void tear_down() override {
auto status = td::walk_path("A/", [&](CSlice path, bool is_dir) { td::walk_path("A/",
[&](CSlice path, bool is_dir) {
if (is_dir) { if (is_dir) {
rmdir(path).ignore(); rmdir(path).ignore();
} else { } else {
unlink(path).ignore(); unlink(path).ignore();
} }
}); })
.ignore();
} }
}; };

View File

@ -17,7 +17,7 @@ int main(int argc, char *argv[]) {
auto status = td::walk_path(dir, [&](td::CSlice path, bool is_dir) { auto status = td::walk_path(dir, [&](td::CSlice path, bool is_dir) {
cnt++; cnt++;
LOG(INFO) << path << " " << is_dir; LOG(INFO) << path << " " << is_dir;
// if (is_dir) { //if (is_dir) {
// td::rmdir(path); // td::rmdir(path);
//} else { //} else {
// td::unlink(path); // td::unlink(path);

View File

@ -67,7 +67,7 @@ Status mkpath(CSlice path, int32 mode) {
} }
Status rmrf(CSlice path) { Status rmrf(CSlice path) {
return walk_path(path, [&](CSlice path, bool is_dir) { return walk_path(path, [](CSlice path, bool is_dir) {
if (is_dir) { if (is_dir) {
return rmdir(path); return rmdir(path);
} else { } else {

View File

@ -16,8 +16,6 @@
#include "td/utils/Slice.h" #include "td/utils/Slice.h"
#include "td/utils/Status.h" #include "td/utils/Status.h"
#include <utility>
#if TD_PORT_POSIX #if TD_PORT_POSIX
#include <limits.h> #include <limits.h>
#include <dirent.h> #include <dirent.h>
@ -50,23 +48,23 @@ Result<std::pair<FileFd, string>> mkstemp(CSlice dir) TD_WARN_UNUSED_RESULT;
Result<string> mkdtemp(CSlice dir, Slice prefix) TD_WARN_UNUSED_RESULT; Result<string> mkdtemp(CSlice dir, Slice prefix) TD_WARN_UNUSED_RESULT;
template <class Func> template <class Func>
Status walk_path(CSlice path, Func &&func) TD_WARN_UNUSED_RESULT; Status walk_path(CSlice path, const Func &func) TD_WARN_UNUSED_RESULT;
#if TD_PORT_POSIX #if TD_PORT_POSIX
// TODO move details somewhere else // TODO move details somewhere else
namespace detail { namespace detail {
template <class Func> template <class Func>
Status walk_path_dir(string &path, FileFd fd, Func &&func) TD_WARN_UNUSED_RESULT; Status walk_path_dir(string &path, FileFd fd, const Func &func) TD_WARN_UNUSED_RESULT;
template <class Func> template <class Func>
Status walk_path_dir(string &path, Func &&func) TD_WARN_UNUSED_RESULT; Status walk_path_dir(string &path, const Func &func) TD_WARN_UNUSED_RESULT;
template <class Func> template <class Func>
Status walk_path_file(string &path, Func &&func) TD_WARN_UNUSED_RESULT; Status walk_path_file(string &path, const Func &func) TD_WARN_UNUSED_RESULT;
template <class Func> template <class Func>
Status walk_path(string &path, Func &&func) TD_WARN_UNUSED_RESULT; Status walk_path(string &path, const Func &func) TD_WARN_UNUSED_RESULT;
template <class Func> template <class Func>
Status walk_path_subdir(string &path, DIR *dir, Func &&func) { Status walk_path_subdir(string &path, DIR *dir, const Func &func) {
while (true) { while (true) {
errno = 0; errno = 0;
auto *entry = readdir(dir); auto *entry = readdir(dir);
@ -77,7 +75,7 @@ Status walk_path_subdir(string &path, DIR *dir, Func &&func) {
if (entry == nullptr) { if (entry == nullptr) {
return Status::OK(); return Status::OK();
} }
Slice name = Slice(&*entry->d_name); Slice name = Slice(static_cast<const char *>(entry->d_name));
if (name == "." || name == "..") { if (name == "." || name == "..") {
continue; continue;
} }
@ -92,15 +90,15 @@ Status walk_path_subdir(string &path, DIR *dir, Func &&func) {
Status status; Status status;
#ifdef DT_DIR #ifdef DT_DIR
if (entry->d_type == DT_UNKNOWN) { if (entry->d_type == DT_UNKNOWN) {
status = walk_path(path, std::forward<Func>(func)); status = walk_path(path, func);
} else if (entry->d_type == DT_DIR) { } else if (entry->d_type == DT_DIR) {
status = walk_path_dir(path, std::forward<Func>(func)); status = walk_path_dir(path, func);
} else if (entry->d_type == DT_REG) { } else if (entry->d_type == DT_REG) {
status = walk_path_file(path, std::forward<Func>(func)); status = walk_path_file(path, func);
} }
#else #else
#warning "Slow walk_path" #warning "Slow walk_path"
status = walk_path(path, std::forward<Func>(func)); status = walk_path(path, func);
#endif #endif
if (status.is_error()) { if (status.is_error()) {
return status; return status;
@ -109,55 +107,54 @@ Status walk_path_subdir(string &path, DIR *dir, Func &&func) {
} }
template <class Func> template <class Func>
Status walk_path_dir(string &path, DIR *subdir, Func &&func) { Status walk_path_dir(string &path, DIR *subdir, const Func &func) {
SCOPE_EXIT { SCOPE_EXIT {
closedir(subdir); closedir(subdir);
}; };
TRY_STATUS(walk_path_subdir(path, subdir, std::forward<Func>(func))); TRY_STATUS(walk_path_subdir(path, subdir, func));
std::forward<Func>(func)(path, true); func(path, true);
return Status::OK(); return Status::OK();
} }
template <class Func> template <class Func>
Status walk_path_dir(string &path, FileFd fd, Func &&func) { Status walk_path_dir(string &path, FileFd fd, const Func &func) {
auto native_fd = fd.move_as_native_fd(); auto native_fd = fd.move_as_native_fd();
auto *subdir = fdopendir(native_fd.fd()); auto *subdir = fdopendir(native_fd.fd());
if (subdir == nullptr) { if (subdir == nullptr) {
auto error = OS_ERROR("fdopendir"); return OS_ERROR("fdopendir");
return error;
} }
native_fd.release(); native_fd.release();
return walk_path_dir(path, subdir, std::forward<Func>(func)); return walk_path_dir(path, subdir, func);
} }
template <class Func> template <class Func>
Status walk_path_dir(string &path, Func &&func) { Status walk_path_dir(string &path, const Func &func) {
auto *subdir = opendir(path.c_str()); auto *subdir = opendir(path.c_str());
if (subdir == nullptr) { if (subdir == nullptr) {
return OS_ERROR(PSLICE() << tag("opendir", path)); return OS_ERROR(PSLICE() << tag("opendir", path));
} }
return walk_path_dir(path, subdir, std::forward<Func>(func)); return walk_path_dir(path, subdir, func);
} }
template <class Func> template <class Func>
Status walk_path_file(string &path, Func &&func) { Status walk_path_file(string &path, const Func &func) {
std::forward<Func>(func)(path, false); func(path, false);
return Status::OK(); return Status::OK();
} }
template <class Func> template <class Func>
Status walk_path(string &path, Func &&func) { Status walk_path(string &path, const Func &func) {
TRY_RESULT(fd, FileFd::open(path, FileFd::Read)); TRY_RESULT(fd, FileFd::open(path, FileFd::Read));
auto stat = fd.stat(); auto stat = fd.stat();
bool is_dir = stat.is_dir_; bool is_dir = stat.is_dir_;
bool is_reg = stat.is_reg_; bool is_reg = stat.is_reg_;
if (is_dir) { if (is_dir) {
return walk_path_dir(path, std::move(fd), std::forward<Func>(func)); return walk_path_dir(path, std::move(fd), func);
} }
fd.close(); fd.close();
if (is_reg) { if (is_reg) {
return walk_path_file(path, std::forward<Func>(func)); return walk_path_file(path, func);
} }
return Status::OK(); return Status::OK();
@ -165,11 +162,11 @@ Status walk_path(string &path, Func &&func) {
} // namespace detail } // namespace detail
template <class Func> template <class Func>
Status walk_path(CSlice path, Func &&func) { Status walk_path(CSlice path, const Func &func) {
string curr_path; string curr_path;
curr_path.reserve(PATH_MAX + 10); curr_path.reserve(PATH_MAX + 10);
curr_path = path.c_str(); curr_path = path.c_str();
return detail::walk_path(curr_path, std::forward<Func>(func)); return detail::walk_path(curr_path, func);
} }
#endif #endif
@ -178,7 +175,7 @@ Status walk_path(CSlice path, Func &&func) {
namespace detail { namespace detail {
template <class Func> template <class Func>
Status walk_path_dir(const std::wstring &dir_name, Func &&func) { Status walk_path_dir(const std::wstring &dir_name, const Func &func) {
std::wstring name = dir_name + L"\\*"; std::wstring name = dir_name + L"\\*";
WIN32_FIND_DATA file_data; WIN32_FIND_DATA file_data;
@ -196,7 +193,6 @@ Status walk_path_dir(const std::wstring &dir_name, Func &&func) {
if (file_data.cFileName[0] != '.') { if (file_data.cFileName[0] != '.') {
if ((file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) { if ((file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
TRY_STATUS(walk_path_dir(full_name, func)); TRY_STATUS(walk_path_dir(full_name, func));
func(entry_name, true);
} else { } else {
func(entry_name, false); func(entry_name, false);
} }
@ -205,23 +201,26 @@ Status walk_path_dir(const std::wstring &dir_name, Func &&func) {
if (status == 0) { if (status == 0) {
auto last_error = GetLastError(); auto last_error = GetLastError();
if (last_error == ERROR_NO_MORE_FILES) { if (last_error == ERROR_NO_MORE_FILES) {
return Status::OK(); break;
} }
return OS_ERROR("FindNextFileW"); return OS_ERROR("FindNextFileW");
} }
} }
TRY_RESULT(entry_name, from_wstring(dir_name));
func(entry_name, true);
return Status::OK();
} }
} // namespace detail } // namespace detail
template <class Func> template <class Func>
Status walk_path(CSlice path, Func &&func) { Status walk_path(CSlice path, const Func &func) {
TRY_RESULT(wpath, to_wstring(path)); TRY_RESULT(wpath, to_wstring(path));
Slice path_slice = path; Slice path_slice = path;
while (!path_slice.empty() && (path_slice.back() == '/' || path_slice.back() == '\\')) { while (!path_slice.empty() && (path_slice.back() == '/' || path_slice.back() == '\\')) {
path_slice.remove_suffix(1); path_slice.remove_suffix(1);
wpath.pop_back(); wpath.pop_back();
} }
return detail::walk_path_dir(wpath.c_str(), std::forward<Func>(func)); return detail::walk_path_dir(wpath.c_str(), func);
} }
#endif #endif

View File

@ -17,14 +17,21 @@ TEST(Port, files) {
CSlice main_dir = "test_dir"; CSlice main_dir = "test_dir";
rmrf(main_dir).ignore(); rmrf(main_dir).ignore();
ASSERT_TRUE(FileFd::open(main_dir, FileFd::Write).is_error()); ASSERT_TRUE(FileFd::open(main_dir, FileFd::Write).is_error());
ASSERT_TRUE(walk_path(main_dir, [](CSlice name, bool is_directory) {
UNREACHABLE();
}).is_error());
mkdir(main_dir).ensure(); mkdir(main_dir).ensure();
mkdir(PSLICE() << main_dir << TD_DIR_SLASH << "A").ensure(); mkdir(PSLICE() << main_dir << TD_DIR_SLASH << "A").ensure();
mkdir(PSLICE() << main_dir << TD_DIR_SLASH << "B").ensure(); mkdir(PSLICE() << main_dir << TD_DIR_SLASH << "B").ensure();
mkdir(PSLICE() << main_dir << TD_DIR_SLASH << "B" << TD_DIR_SLASH << "D").ensure();
mkdir(PSLICE() << main_dir << TD_DIR_SLASH << "C").ensure(); mkdir(PSLICE() << main_dir << TD_DIR_SLASH << "C").ensure();
ASSERT_TRUE(FileFd::open(main_dir, FileFd::Write).is_error()); ASSERT_TRUE(FileFd::open(main_dir, FileFd::Write).is_error());
std::string fd_path = PSTRING() << main_dir << TD_DIR_SLASH << "t.txt"; std::string fd_path = PSTRING() << main_dir << TD_DIR_SLASH << "t.txt";
std::string fd2_path = PSTRING() << main_dir << TD_DIR_SLASH << "C" << TD_DIR_SLASH << "t2.txt";
auto fd = FileFd::open(fd_path, FileFd::Write | FileFd::CreateNew).move_as_ok(); auto fd = FileFd::open(fd_path, FileFd::Write | FileFd::CreateNew).move_as_ok();
auto fd2 = FileFd::open(fd2_path, FileFd::Write | FileFd::CreateNew).move_as_ok();
fd2.close();
int cnt = 0; int cnt = 0;
const int ITER_COUNT = 1000; const int ITER_COUNT = 1000;
@ -32,13 +39,13 @@ TEST(Port, files) {
walk_path(main_dir, walk_path(main_dir,
[&](CSlice name, bool is_directory) { [&](CSlice name, bool is_directory) {
if (!is_directory) { if (!is_directory) {
ASSERT_EQ(fd_path, name); ASSERT_TRUE(name == fd_path || name == fd2_path);
} }
cnt++; cnt++;
}) })
.ensure(); .ensure();
} }
ASSERT_EQ(4 * ITER_COUNT, cnt); ASSERT_EQ(7 * ITER_COUNT, cnt);
ASSERT_EQ(0u, fd.get_size()); ASSERT_EQ(0u, fd.get_size());
ASSERT_EQ(12u, fd.write("Hello world!").move_as_ok()); ASSERT_EQ(12u, fd.write("Hello world!").move_as_ok());