2121
2222#include < cerrno>
2323#include < cstdio>
24+ #include < set>
2425#include < string>
26+ #include < utility>
2527
2628#include " absl/functional/function_ref.h"
2729#include " absl/status/status.h"
@@ -36,7 +38,33 @@ namespace {
3638
3739absl::Status TraverseDirectory (
3840 absl::string_view path,
39- absl::FunctionRef<void (absl::string_view, const struct stat &)> callback) {
41+ absl::FunctionRef<void (absl::string_view, const struct stat &)> callback,
42+ std::set<std::pair<dev_t, ino_t>>& visited);
43+
44+ absl::Status YieldFilesInternal (
45+ absl::string_view path,
46+ absl::FunctionRef<void (absl::string_view, const struct stat &)> callback,
47+ std::set<std::pair<dev_t, ino_t>>& visited) {
48+ struct stat path_stat;
49+ if (stat (std::string (path).c_str (), &path_stat) < 0 ) {
50+ return ErrnoStatus (absl::StrCat (" could not stat " , path), errno);
51+ }
52+ if (S_ISDIR (path_stat.st_mode )) {
53+ auto dir_id = std::make_pair (path_stat.st_dev , path_stat.st_ino );
54+ // Prevent infinite recursion by tracking visited directories (dev,inode).
55+ if (!visited.insert (dir_id).second ) {
56+ return absl::OkStatus ();
57+ }
58+ return TraverseDirectory (path, callback, visited);
59+ }
60+ callback (path, path_stat);
61+ return absl::OkStatus ();
62+ }
63+
64+ absl::Status TraverseDirectory (
65+ absl::string_view path,
66+ absl::FunctionRef<void (absl::string_view, const struct stat &)> callback,
67+ std::set<std::pair<dev_t, ino_t>>& visited) {
4068 DIR * dir = opendir (std::string (path).c_str ());
4169 if (!dir) {
4270 return ErrnoStatus (absl::StrCat (" could not open directory " , path), errno);
@@ -58,7 +86,7 @@ absl::Status TraverseDirectory(
5886 continue ;
5987 }
6088 const std::string entry_path = absl::StrCat (path, " /" , entry_name);
61- status.Update (YieldFiles (entry_path, callback));
89+ status.Update (YieldFilesInternal (entry_path, callback, visited ));
6290 }
6391 closedir (dir);
6492 return status;
@@ -69,15 +97,8 @@ absl::Status TraverseDirectory(
6997absl::Status YieldFiles (
7098 absl::string_view path,
7199 absl::FunctionRef<void (absl::string_view, const struct stat &)> callback) {
72- struct stat path_stat;
73- if (stat (std::string (path).c_str (), &path_stat) < 0 ) {
74- return ErrnoStatus (absl::StrCat (" could not stat " , path), errno);
75- }
76- if (S_ISDIR (path_stat.st_mode )) {
77- return TraverseDirectory (path, callback);
78- }
79- callback (path, path_stat);
80- return absl::OkStatus ();
100+ std::set<std::pair<dev_t , ino_t >> visited;
101+ return YieldFilesInternal (path, callback, visited);
81102}
82103
83104absl::Status SetFileContents (absl::string_view path,
0 commit comments