Index: runtime/bin/directory_linux.cc |
diff --git a/runtime/bin/directory_linux.cc b/runtime/bin/directory_linux.cc |
index 9484b1e61c71aa75414dc7832402cb41018c6788..2f86f5be475729d9f6273ea72c218e5a3ba8b800 100644 |
--- a/runtime/bin/directory_linux.cc |
+++ b/runtime/bin/directory_linux.cc |
@@ -54,10 +54,20 @@ class PathBuffer { |
}; |
+// A linked list of symbolic links, with their unique file system identifiers. |
+// These are scanned to detect loops while doing a recursive directory listing. |
+struct LinkList { |
+ dev_t dev; |
+ ino_t ino; |
+ LinkList* next; |
+}; |
+ |
+ |
// Forward declarations. |
static bool ListRecursively(PathBuffer* path, |
bool recursive, |
bool follow_links, |
+ LinkList* seen, |
DirectoryListing* listing); |
static bool DeleteRecursively(PathBuffer* path); |
@@ -72,6 +82,7 @@ static bool HandleDir(char* dir_name, |
PathBuffer* path, |
bool recursive, |
bool follow_links, |
+ LinkList* seen, |
DirectoryListing *listing) { |
if (strcmp(dir_name, ".") == 0) return true; |
if (strcmp(dir_name, "..") == 0) return true; |
@@ -80,7 +91,8 @@ static bool HandleDir(char* dir_name, |
return false; |
} |
return listing->HandleDirectory(path->data) && |
- (!recursive || ListRecursively(path, recursive, follow_links, listing)); |
+ (!recursive || |
+ ListRecursively(path, recursive, follow_links, seen, listing)); |
} |
@@ -109,6 +121,7 @@ static bool HandleLink(char* link_name, |
static bool ListRecursively(PathBuffer* path, |
bool recursive, |
bool follow_links, |
+ LinkList* seen, |
DirectoryListing *listing) { |
if (!path->Add(File::PathSeparator())) { |
PostError(listing, path->data); |
@@ -140,6 +153,7 @@ static bool ListRecursively(PathBuffer* path, |
path, |
recursive, |
follow_links, |
+ seen, |
listing) && success; |
break; |
case DT_REG: |
@@ -167,25 +181,62 @@ static bool ListRecursively(PathBuffer* path, |
break; |
} |
int stat_success; |
- if (follow_links) { |
- stat_success = TEMP_FAILURE_RETRY(stat(path->data, &entry_info)); |
- if (stat_success == -1) { |
- stat_success = TEMP_FAILURE_RETRY(lstat(path->data, &entry_info)); |
- } |
- } else { |
- stat_success = TEMP_FAILURE_RETRY(lstat(path->data, &entry_info)); |
- } |
+ stat_success = TEMP_FAILURE_RETRY(lstat(path->data, &entry_info)); |
if (stat_success == -1) { |
success = false; |
PostError(listing, path->data); |
break; |
} |
+ if (follow_links && S_ISLNK(entry_info.st_mode)) { |
+ // Check to see if we are in a loop created by a symbolic link. |
+ LinkList current_link = { entry_info.st_dev, |
+ entry_info.st_ino, |
+ seen }; |
+ LinkList* previous = seen; |
+ bool looping_link = false; |
+ while (previous != NULL) { |
+ if (previous->dev == current_link.dev && |
+ previous->ino == current_link.ino) { |
Søren Gjesse
2013/04/10 11:33:23
How about adding a compare method (or operator==)
Bill Hesse
2013/04/10 12:49:47
I would do that if we had another use. But that t
|
+ // Report the looping link as a link, rather than following it. |
+ path->Reset(path_length); |
+ success = HandleLink(entry.d_name, |
+ path, |
+ listing) && success; |
+ looping_link = true; |
+ break; |
+ } |
+ previous = previous->next; |
+ } |
+ if (looping_link) break; |
+ stat_success = TEMP_FAILURE_RETRY(stat(path->data, &entry_info)); |
+ if (stat_success == -1) { |
+ // Report a broken link as a link, even if follow_links is true. |
+ path->Reset(path_length); |
+ success = HandleLink(entry.d_name, |
+ path, |
+ listing) && success; |
+ break; |
+ } |
+ if (S_ISDIR(entry_info.st_mode)) { |
+ // Recurse into the subdirectory with current_link added to the |
+ // linked list of seen file system links. |
+ path->Reset(path_length); |
+ success = HandleDir(entry.d_name, |
+ path, |
+ recursive, |
+ follow_links, |
+ ¤t_link, |
+ listing) && success; |
+ break; |
+ } |
+ } |
path->Reset(path_length); |
if (S_ISDIR(entry_info.st_mode)) { |
success = HandleDir(entry.d_name, |
path, |
recursive, |
follow_links, |
+ seen, |
listing) && success; |
} else if (S_ISREG(entry_info.st_mode)) { |
success = HandleFile(entry.d_name, |
@@ -327,7 +378,7 @@ bool Directory::List(const char* dir_name, |
PostError(listing, dir_name); |
return false; |
} |
- return ListRecursively(&path, recursive, follow_links, listing); |
+ return ListRecursively(&path, recursive, follow_links, NULL, listing); |
} |