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,36 @@ 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+ callback (path, path_stat);
53+ if (S_ISDIR (path_stat.st_mode )) {
54+ auto dir_id = std::make_pair (path_stat.st_dev , path_stat.st_ino );
55+ if (!visited.count (dir_id)) {
56+ // Prevent infinite recursion by tracking visited directories (dev,inode).
57+ visited.insert (dir_id);
58+ absl::Status status = TraverseDirectory (path, callback, visited);
59+ if (!status.ok ()) {
60+ return status;
61+ }
62+ }
63+ }
64+ return absl::OkStatus ();
65+ }
66+
67+ absl::Status TraverseDirectory (
68+ absl::string_view path,
69+ absl::FunctionRef<void (absl::string_view, const struct stat &)> callback,
70+ std::set<std::pair<dev_t, ino_t>>& visited) {
4071 DIR * dir = opendir (std::string (path).c_str ());
4172 if (!dir) {
4273 return ErrnoStatus (absl::StrCat (" could not open directory " , path), errno);
@@ -58,7 +89,7 @@ absl::Status TraverseDirectory(
5889 continue ;
5990 }
6091 const std::string entry_path = absl::StrCat (path, " /" , entry_name);
61- status.Update (YieldFiles (entry_path, callback));
92+ status.Update (YieldFilesInternal (entry_path, callback, visited ));
6293 }
6394 closedir (dir);
6495 return status;
@@ -69,15 +100,8 @@ absl::Status TraverseDirectory(
69100absl::Status YieldFiles (
70101 absl::string_view path,
71102 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 ();
103+ std::set<std::pair<dev_t , ino_t >> visited;
104+ return YieldFilesInternal (path, callback, visited);
81105}
82106
83107absl::Status SetFileContents (absl::string_view path,
0 commit comments