Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(311)

Side by Side Diff: runtime/bin/directory_linux.cc

Issue 13818010: dart:io | Ensure that Directory.list terminates even when symbolic links create loops in the file s… (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Add documentation of new behavior. Created 7 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 #include "platform/globals.h" 5 #include "platform/globals.h"
6 #if defined(TARGET_OS_LINUX) 6 #if defined(TARGET_OS_LINUX)
7 7
8 #include "bin/directory.h" 8 #include "bin/directory.h"
9 9
10 #include <dirent.h> // NOLINT 10 #include <dirent.h> // NOLINT
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
47 } 47 }
48 } 48 }
49 49
50 void Reset(int new_length) { 50 void Reset(int new_length) {
51 length = new_length; 51 length = new_length;
52 data[length] = '\0'; 52 data[length] = '\0';
53 } 53 }
54 }; 54 };
55 55
56 56
57 // A linked list of symbolic links, with their unique file system identifiers.
58 // These are scanned to detect loops while doing a recursive directory listing.
59 struct LinkList {
60 dev_t dev;
61 ino_t ino;
62 LinkList* next;
63 };
64
65
57 // Forward declarations. 66 // Forward declarations.
58 static bool ListRecursively(PathBuffer* path, 67 static bool ListRecursively(PathBuffer* path,
59 bool recursive, 68 bool recursive,
60 bool follow_links, 69 bool follow_links,
70 LinkList* seen,
61 DirectoryListing* listing); 71 DirectoryListing* listing);
62 static bool DeleteRecursively(PathBuffer* path); 72 static bool DeleteRecursively(PathBuffer* path);
63 73
64 74
65 static void PostError(DirectoryListing *listing, 75 static void PostError(DirectoryListing *listing,
66 const char* dir_name) { 76 const char* dir_name) {
67 listing->HandleError(dir_name); 77 listing->HandleError(dir_name);
68 } 78 }
69 79
70 80
71 static bool HandleDir(char* dir_name, 81 static bool HandleDir(char* dir_name,
72 PathBuffer* path, 82 PathBuffer* path,
73 bool recursive, 83 bool recursive,
74 bool follow_links, 84 bool follow_links,
85 LinkList* seen,
75 DirectoryListing *listing) { 86 DirectoryListing *listing) {
76 if (strcmp(dir_name, ".") == 0) return true; 87 if (strcmp(dir_name, ".") == 0) return true;
77 if (strcmp(dir_name, "..") == 0) return true; 88 if (strcmp(dir_name, "..") == 0) return true;
78 if (!path->Add(dir_name)) { 89 if (!path->Add(dir_name)) {
79 PostError(listing, path->data); 90 PostError(listing, path->data);
80 return false; 91 return false;
81 } 92 }
82 return listing->HandleDirectory(path->data) && 93 return listing->HandleDirectory(path->data) &&
83 (!recursive || ListRecursively(path, recursive, follow_links, listing)); 94 (!recursive ||
95 ListRecursively(path, recursive, follow_links, seen, listing));
84 } 96 }
85 97
86 98
87 static bool HandleFile(char* file_name, 99 static bool HandleFile(char* file_name,
88 PathBuffer* path, 100 PathBuffer* path,
89 DirectoryListing *listing) { 101 DirectoryListing *listing) {
90 if (!path->Add(file_name)) { 102 if (!path->Add(file_name)) {
91 PostError(listing, path->data); 103 PostError(listing, path->data);
92 return false; 104 return false;
93 } 105 }
94 return listing->HandleFile(path->data); 106 return listing->HandleFile(path->data);
95 } 107 }
96 108
97 109
98 static bool HandleLink(char* link_name, 110 static bool HandleLink(char* link_name,
99 PathBuffer* path, 111 PathBuffer* path,
100 DirectoryListing *listing) { 112 DirectoryListing *listing) {
101 if (!path->Add(link_name)) { 113 if (!path->Add(link_name)) {
102 PostError(listing, path->data); 114 PostError(listing, path->data);
103 return false; 115 return false;
104 } 116 }
105 return listing->HandleLink(path->data); 117 return listing->HandleLink(path->data);
106 } 118 }
107 119
108 120
109 static bool ListRecursively(PathBuffer* path, 121 static bool ListRecursively(PathBuffer* path,
110 bool recursive, 122 bool recursive,
111 bool follow_links, 123 bool follow_links,
124 LinkList* seen,
112 DirectoryListing *listing) { 125 DirectoryListing *listing) {
113 if (!path->Add(File::PathSeparator())) { 126 if (!path->Add(File::PathSeparator())) {
114 PostError(listing, path->data); 127 PostError(listing, path->data);
115 return false; 128 return false;
116 } 129 }
117 DIR* dir_pointer; 130 DIR* dir_pointer;
118 do { 131 do {
119 dir_pointer = opendir(path->data); 132 dir_pointer = opendir(path->data);
120 } while (dir_pointer == NULL && errno == EINTR); 133 } while (dir_pointer == NULL && errno == EINTR);
121 if (dir_pointer == NULL) { 134 if (dir_pointer == NULL) {
(...skipping 11 matching lines...) Expand all
133 while ((status = TEMP_FAILURE_RETRY(readdir_r(dir_pointer, 146 while ((status = TEMP_FAILURE_RETRY(readdir_r(dir_pointer,
134 &entry, 147 &entry,
135 &result))) == 0 && 148 &result))) == 0 &&
136 result != NULL) { 149 result != NULL) {
137 switch (entry.d_type) { 150 switch (entry.d_type) {
138 case DT_DIR: 151 case DT_DIR:
139 success = HandleDir(entry.d_name, 152 success = HandleDir(entry.d_name,
140 path, 153 path,
141 recursive, 154 recursive,
142 follow_links, 155 follow_links,
156 seen,
143 listing) && success; 157 listing) && success;
144 break; 158 break;
145 case DT_REG: 159 case DT_REG:
146 success = HandleFile(entry.d_name, 160 success = HandleFile(entry.d_name,
147 path, 161 path,
148 listing) && success; 162 listing) && success;
149 break; 163 break;
150 case DT_LNK: 164 case DT_LNK:
151 if (!follow_links) { 165 if (!follow_links) {
152 success = HandleLink(entry.d_name, 166 success = HandleLink(entry.d_name,
153 path, 167 path,
154 listing) && success; 168 listing) && success;
155 break; 169 break;
156 } 170 }
157 // Else fall through to next case. 171 // Else fall through to next case.
158 // Fall through. 172 // Fall through.
159 case DT_UNKNOWN: { 173 case DT_UNKNOWN: {
160 // On some file systems the entry type is not determined by 174 // On some file systems the entry type is not determined by
161 // readdir_r. For those and for links we use stat to determine 175 // readdir_r. For those and for links we use stat to determine
162 // the actual entry type. Notice that stat returns the type of 176 // the actual entry type. Notice that stat returns the type of
163 // the file pointed to. 177 // the file pointed to.
164 struct stat entry_info; 178 struct stat entry_info;
165 if (!path->Add(entry.d_name)) { 179 if (!path->Add(entry.d_name)) {
166 success = false; 180 success = false;
167 break; 181 break;
168 } 182 }
169 int stat_success; 183 int stat_success;
170 if (follow_links) { 184 stat_success = TEMP_FAILURE_RETRY(lstat(path->data, &entry_info));
171 stat_success = TEMP_FAILURE_RETRY(stat(path->data, &entry_info));
172 if (stat_success == -1) {
173 stat_success = TEMP_FAILURE_RETRY(lstat(path->data, &entry_info));
174 }
175 } else {
176 stat_success = TEMP_FAILURE_RETRY(lstat(path->data, &entry_info));
177 }
178 if (stat_success == -1) { 185 if (stat_success == -1) {
179 success = false; 186 success = false;
180 PostError(listing, path->data); 187 PostError(listing, path->data);
181 break; 188 break;
182 } 189 }
190 if (follow_links && S_ISLNK(entry_info.st_mode)) {
191 // Check to see if we are in a loop created by a symbolic link.
192 LinkList current_link = { entry_info.st_dev,
193 entry_info.st_ino,
194 seen };
195 LinkList* previous = seen;
196 bool looping_link = false;
197 while (previous != NULL) {
198 if (previous->dev == current_link.dev &&
199 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
200 // Report the looping link as a link, rather than following it.
201 path->Reset(path_length);
202 success = HandleLink(entry.d_name,
203 path,
204 listing) && success;
205 looping_link = true;
206 break;
207 }
208 previous = previous->next;
209 }
210 if (looping_link) break;
211 stat_success = TEMP_FAILURE_RETRY(stat(path->data, &entry_info));
212 if (stat_success == -1) {
213 // Report a broken link as a link, even if follow_links is true.
214 path->Reset(path_length);
215 success = HandleLink(entry.d_name,
216 path,
217 listing) && success;
218 break;
219 }
220 if (S_ISDIR(entry_info.st_mode)) {
221 // Recurse into the subdirectory with current_link added to the
222 // linked list of seen file system links.
223 path->Reset(path_length);
224 success = HandleDir(entry.d_name,
225 path,
226 recursive,
227 follow_links,
228 &current_link,
229 listing) && success;
230 break;
231 }
232 }
183 path->Reset(path_length); 233 path->Reset(path_length);
184 if (S_ISDIR(entry_info.st_mode)) { 234 if (S_ISDIR(entry_info.st_mode)) {
185 success = HandleDir(entry.d_name, 235 success = HandleDir(entry.d_name,
186 path, 236 path,
187 recursive, 237 recursive,
188 follow_links, 238 follow_links,
239 seen,
189 listing) && success; 240 listing) && success;
190 } else if (S_ISREG(entry_info.st_mode)) { 241 } else if (S_ISREG(entry_info.st_mode)) {
191 success = HandleFile(entry.d_name, 242 success = HandleFile(entry.d_name,
192 path, 243 path,
193 listing) && success; 244 listing) && success;
194 } else if (S_ISLNK(entry_info.st_mode)) { 245 } else if (S_ISLNK(entry_info.st_mode)) {
195 success = HandleLink(entry.d_name, 246 success = HandleLink(entry.d_name,
196 path, 247 path,
197 listing) && success; 248 listing) && success;
198 } 249 }
(...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after
320 371
321 bool Directory::List(const char* dir_name, 372 bool Directory::List(const char* dir_name,
322 bool recursive, 373 bool recursive,
323 bool follow_links, 374 bool follow_links,
324 DirectoryListing *listing) { 375 DirectoryListing *listing) {
325 PathBuffer path; 376 PathBuffer path;
326 if (!path.Add(dir_name)) { 377 if (!path.Add(dir_name)) {
327 PostError(listing, dir_name); 378 PostError(listing, dir_name);
328 return false; 379 return false;
329 } 380 }
330 return ListRecursively(&path, recursive, follow_links, listing); 381 return ListRecursively(&path, recursive, follow_links, NULL, listing);
331 } 382 }
332 383
333 384
334 Directory::ExistsResult Directory::Exists(const char* dir_name) { 385 Directory::ExistsResult Directory::Exists(const char* dir_name) {
335 struct stat entry_info; 386 struct stat entry_info;
336 int success = TEMP_FAILURE_RETRY(stat(dir_name, &entry_info)); 387 int success = TEMP_FAILURE_RETRY(stat(dir_name, &entry_info));
337 if (success == 0) { 388 if (success == 0) {
338 if (S_ISDIR(entry_info.st_mode)) { 389 if (S_ISDIR(entry_info.st_mode)) {
339 return EXISTS; 390 return EXISTS;
340 } else { 391 } else {
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after
425 } 476 }
426 477
427 478
428 bool Directory::Rename(const char* path, const char* new_path) { 479 bool Directory::Rename(const char* path, const char* new_path) {
429 ExistsResult exists = Exists(path); 480 ExistsResult exists = Exists(path);
430 if (exists != EXISTS) return false; 481 if (exists != EXISTS) return false;
431 return (TEMP_FAILURE_RETRY(rename(path, new_path)) == 0); 482 return (TEMP_FAILURE_RETRY(rename(path, new_path)) == 0);
432 } 483 }
433 484
434 #endif // defined(TARGET_OS_LINUX) 485 #endif // defined(TARGET_OS_LINUX)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698