Index: runtime/bin/directory_win.cc |
diff --git a/runtime/bin/directory_win.cc b/runtime/bin/directory_win.cc |
index ff99f76385a60a1273d3bfac9987929303b5488e..79c7b082d9b83392843fbdc74c30164be28b0a74 100644 |
--- a/runtime/bin/directory_win.cc |
+++ b/runtime/bin/directory_win.cc |
@@ -68,11 +68,20 @@ static bool IsBrokenLink(const wchar_t* link_name) { |
} |
} |
+// A linked list structure holding a link target's unique file system ID. |
+// Used to detect loops in the file system when listing recursively. |
+struct LinkList { |
+ DWORD volume; |
+ DWORD id_low; |
+ DWORD id_high; |
+ LinkList* next; |
+}; |
// Forward declarations. |
static bool ListRecursively(PathBuffer* path, |
bool recursive, |
bool follow_links, |
+ LinkList* seen, |
DirectoryListing* listing); |
static bool DeleteRecursively(PathBuffer* path); |
@@ -89,6 +98,7 @@ static bool HandleDir(wchar_t* dir_name, |
PathBuffer* path, |
bool recursive, |
bool follow_links, |
+ LinkList* seen, |
DirectoryListing* listing) { |
if (wcscmp(dir_name, L".") == 0) return true; |
if (wcscmp(dir_name, L"..") == 0) return true; |
@@ -100,7 +110,8 @@ static bool HandleDir(wchar_t* dir_name, |
bool ok = listing->HandleDirectory(utf8_path); |
free(utf8_path); |
return ok && |
- (!recursive || ListRecursively(path, recursive, follow_links, listing)); |
+ (!recursive || |
+ ListRecursively(path, recursive, follow_links, seen, listing)); |
} |
@@ -136,6 +147,7 @@ static bool HandleEntry(LPWIN32_FIND_DATAW find_file_data, |
PathBuffer* path, |
bool recursive, |
bool follow_links, |
+ LinkList* seen, |
DirectoryListing* listing) { |
DWORD attributes = find_file_data->dwFileAttributes; |
if ((attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0) { |
@@ -144,18 +156,61 @@ static bool HandleEntry(LPWIN32_FIND_DATAW find_file_data, |
} |
int path_length = path->length; |
if (!path->Add(find_file_data->cFileName)) return false; |
- bool broken = IsBrokenLink(path->data); |
+ HANDLE handle = CreateFileW( |
+ path->data, |
+ 0, |
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, |
+ NULL, |
+ OPEN_EXISTING, |
+ FILE_FLAG_BACKUP_SEMANTICS, |
+ NULL); |
path->Reset(path_length); |
- if (broken) { |
+ if (handle == INVALID_HANDLE_VALUE) { |
// Report as (broken) link. |
return HandleLink(find_file_data->cFileName, path, listing); |
} |
+ if ((attributes & FILE_ATTRIBUTE_DIRECTORY) != 0) { |
+ // Check the seen link targets to see if we are in a file system loop. |
+ LinkList current_link; |
+ BY_HANDLE_FILE_INFORMATION info; |
+ // Get info |
+ if (!GetFileInformationByHandle(handle, &info)) { |
+ DWORD error = GetLastError(); |
+ CloseHandle(handle); |
+ SetLastError(error); |
+ PostError(listing, path->data); |
+ return false; |
+ } |
+ CloseHandle(handle); |
+ current_link.volume = info.dwVolumeSerialNumber; |
+ current_link.id_low = info.nFileIndexLow; |
+ current_link.id_high = info.nFileIndexHigh; |
+ current_link.next = seen; |
+ LinkList* previous = seen; |
+ while (previous != NULL) { |
+ if (previous->volume == current_link.volume && |
+ previous->id_low == current_link.id_low && |
+ previous->id_high == current_link.id_high) { |
+ // Report the looping link as a link, rather than following it. |
+ return HandleLink(find_file_data->cFileName, path, listing); |
+ } |
+ previous = previous->next; |
+ } |
+ // Recurse into the directory, adding current link to the seen links list. |
+ return HandleDir(find_file_data->cFileName, |
+ path, |
+ recursive, |
+ follow_links, |
+ ¤t_link, |
+ listing); |
+ } |
} |
if ((attributes & FILE_ATTRIBUTE_DIRECTORY) != 0) { |
return HandleDir(find_file_data->cFileName, |
path, |
recursive, |
follow_links, |
+ seen, |
listing); |
} else { |
return HandleFile(find_file_data->cFileName, path, listing); |
@@ -166,6 +221,7 @@ static bool HandleEntry(LPWIN32_FIND_DATAW find_file_data, |
static bool ListRecursively(PathBuffer* path, |
bool recursive, |
bool follow_links, |
+ LinkList* seen, |
DirectoryListing* listing) { |
if (!path->Add(L"\\*")) { |
PostError(listing, path->data); |
@@ -188,6 +244,7 @@ static bool ListRecursively(PathBuffer* path, |
path, |
recursive, |
follow_links, |
+ seen, |
listing); |
while ((FindNextFileW(find_handle, &find_file_data) != 0)) { |
@@ -196,6 +253,7 @@ static bool ListRecursively(PathBuffer* path, |
path, |
recursive, |
follow_links, |
+ seen, |
listing) && success; |
} |
@@ -320,7 +378,7 @@ bool Directory::List(const char* dir_name, |
return false; |
} |
free(const_cast<wchar_t*>(system_name)); |
- return ListRecursively(&path, recursive, follow_links, listing); |
+ return ListRecursively(&path, recursive, follow_links, NULL, listing); |
} |