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

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

Issue 12220034: Use a PathBuffer class in dart:io directory lister. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Add setting of errno and posting error when path overflows. Created 7 years, 10 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
« no previous file with comments | « runtime/bin/directory_android.cc ('k') | runtime/bin/directory_macos.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 "bin/directory.h" 5 #include "bin/directory.h"
6 6
7 #include <dirent.h> 7 #include <dirent.h>
8 #include <errno.h> 8 #include <errno.h>
9 #include <sys/param.h> 9 #include <sys/param.h>
10 #include <sys/stat.h> 10 #include <sys/stat.h>
11 #include <unistd.h> 11 #include <unistd.h>
12 12
13 #include "bin/file.h" 13 #include "bin/file.h"
14 #include "bin/platform.h" 14 #include "bin/platform.h"
15 15
16 class PathBuffer {
17 public:
18 PathBuffer() : length(0) { }
16 19
17 static char* SafeStrNCpy(char* dest, const char* src, size_t n) { 20
18 strncpy(dest, src, n); 21
19 dest[n - 1] = '\0'; 22 char data[PATH_MAX + 1];
20 return dest; 23 int length;
21 } 24
25 bool Add(const char* name) {
26 size_t written = snprintf(data + length,
27 PATH_MAX - length,
28 "%s",
29 name);
30 data[PATH_MAX] = '\0';
31 if (written == strnlen(name, PATH_MAX + 1)) {
32 length += written;
33 return true;
34 } else {
35 errno = ENAMETOOLONG;
36 return false;
37 }
38 }
39
40 void Reset(int new_length) {
41 length = new_length;
42 data[length] = '\0';
43 }
44 };
22 45
23 46
24 // Forward declarations. 47 // Forward declarations.
25 static bool ListRecursively(const char* dir_name, 48 static bool ListRecursively(const char* dir_name,
26 bool recursive, 49 bool recursive,
27 DirectoryListing* listing); 50 DirectoryListing* listing);
28 static bool DeleteRecursively(const char* dir_name); 51 static bool DeleteRecursively(const char* dir_name);
29 52
30 53
31 static bool ComputeFullPath(const char* dir_name,
32 char* path,
33 int* path_length) {
34 char* abs_path;
35 do {
36 abs_path = realpath(dir_name, path);
37 } while (abs_path == NULL && errno == EINTR);
38 if (abs_path == NULL) {
39 return false;
40 }
41 *path_length = strlen(path);
42 size_t written = snprintf(path + *path_length,
43 PATH_MAX - *path_length,
44 "%s",
45 File::PathSeparator());
46 if (written != strlen(File::PathSeparator())) {
47 return false;
48 }
49 *path_length += written;
50 return true;
51 }
52
53
54 static bool HandleDir(char* dir_name,
55 char* path,
56 int path_length,
57 bool recursive,
58 DirectoryListing *listing) {
59 if (strcmp(dir_name, ".") != 0 &&
60 strcmp(dir_name, "..") != 0) {
61 size_t written = snprintf(path + path_length,
62 PATH_MAX - path_length,
63 "%s",
64 dir_name);
65 if (written != strlen(dir_name)) {
66 return false;
67 }
68 bool ok = listing->HandleDirectory(path);
69 if (!ok) return ok;
70 if (recursive) {
71 return ListRecursively(path, recursive, listing);
72 }
73 }
74 return true;
75 }
76
77
78 static bool HandleFile(char* file_name,
79 char* path,
80 int path_length,
81 DirectoryListing *listing) {
82 // TODO(sgjesse): Pass flags to indicate whether file responses are
83 // needed.
84 size_t written = snprintf(path + path_length,
85 PATH_MAX - path_length,
86 "%s",
87 file_name);
88 if (written != strlen(file_name)) {
89 return false;
90 }
91 return listing->HandleFile(path);
92 }
93
94
95 static void PostError(DirectoryListing *listing, 54 static void PostError(DirectoryListing *listing,
96 const char* dir_name) { 55 const char* dir_name) {
97 listing->HandleError(dir_name); 56 listing->HandleError(dir_name);
98 } 57 }
99 58
100 59
60 static PathBuffer* ComputeFullPath(const char* dir_name) {
61 PathBuffer* path = new PathBuffer();
62 char* abs_path;
63 do {
64 abs_path = realpath(dir_name, path->data);
65 } while (abs_path == NULL && errno == EINTR);
66 if (abs_path == NULL) {
67 delete path;
68 return NULL;
69 }
70 path->length = strnlen(path->data, PATH_MAX);
71 if (path->Add(File::PathSeparator())) {
72 return path;
73 } else {
74 delete path;
75 return NULL;
76 }
77 }
78
79 static bool HandleDir(char* dir_name,
80 PathBuffer* path,
81 bool recursive,
82 DirectoryListing *listing) {
83 if (strcmp(dir_name, ".") == 0) return true;
84 if (strcmp(dir_name, "..") == 0) return true;
85 if (!path->Add(dir_name)) {
86 PostError(listing, path->data);
87 return false;
88 }
89 return listing->HandleDirectory(path->data) &&
90 (!recursive || ListRecursively(path->data, recursive, listing));
91 }
92
93 static bool HandleFile(char* file_name,
94 PathBuffer* path,
95 DirectoryListing *listing) {
96 if (!path->Add(file_name)) {
97 PostError(listing, path->data);
98 return false;
99 }
100 return listing->HandleFile(path->data);
101 }
102
103
101 static bool ListRecursively(const char* dir_name, 104 static bool ListRecursively(const char* dir_name,
102 bool recursive, 105 bool recursive,
103 DirectoryListing *listing) { 106 DirectoryListing *listing) {
104 DIR* dir_pointer; 107 DIR* dir_pointer;
105 do { 108 do {
106 dir_pointer = opendir(dir_name); 109 dir_pointer = opendir(dir_name);
107 } while (dir_pointer == NULL && errno == EINTR); 110 } while (dir_pointer == NULL && errno == EINTR);
108 if (dir_pointer == NULL) { 111 if (dir_pointer == NULL) {
109 PostError(listing, dir_name); 112 PostError(listing, dir_name);
110 return false; 113 return false;
111 } 114 }
112 115
113 // Compute full path for the directory currently being listed. The 116 // Compute full path for the directory currently being listed. The
114 // path buffer will be used to construct the current path in the 117 // path buffer will be used to construct the current path in the
115 // recursive traversal. path_length does not always equal 118 // recursive traversal. path_length does not always equal
116 // strlen(path) but indicates the current prefix of path that is the 119 // strlen(path) but indicates the current prefix of path that is the
117 // path of the current directory in the traversal. 120 // path of the current directory in the traversal.
118 char *path = static_cast<char*>(malloc(PATH_MAX)); 121 PathBuffer* path = ComputeFullPath(dir_name);
119 ASSERT(path != NULL); 122 if (path == NULL) {
120 int path_length = 0;
121 bool valid = ComputeFullPath(dir_name, path, &path_length);
122 if (!valid) {
123 free(path);
124 PostError(listing, dir_name); 123 PostError(listing, dir_name);
125 return false; 124 return false;
126 } 125 }
127 126 // Iterate the directory and post the directories and files to the
128 // Iterated the directory and post the directories and files to the
129 // ports. 127 // ports.
130 int read = 0; 128 int path_length = path->length;
129 int status = 0;
131 bool success = true; 130 bool success = true;
132 dirent entry; 131 dirent entry;
133 dirent* result; 132 dirent* result;
134 while ((read = TEMP_FAILURE_RETRY(readdir_r(dir_pointer, 133 while ((status = TEMP_FAILURE_RETRY(readdir_r(dir_pointer,
135 &entry, 134 &entry,
136 &result))) == 0 && 135 &result))) == 0 &&
137 result != NULL) { 136 result != NULL) {
138 switch (entry.d_type) { 137 switch (entry.d_type) {
139 case DT_DIR: 138 case DT_DIR:
140 success = HandleDir(entry.d_name, 139 success = HandleDir(entry.d_name,
141 path, 140 path,
142 path_length,
143 recursive, 141 recursive,
144 listing) && success; 142 listing) && success;
145 break; 143 break;
146 case DT_REG: 144 case DT_REG:
147 success = HandleFile(entry.d_name, 145 success = HandleFile(entry.d_name,
148 path, 146 path,
149 path_length,
150 listing) && success; 147 listing) && success;
151 break; 148 break;
152 case DT_LNK: 149 case DT_LNK:
153 case DT_UNKNOWN: { 150 case DT_UNKNOWN: {
154 // On some file systems the entry type is not determined by 151 // On some file systems the entry type is not determined by
155 // readdir_r. For those and for links we use stat to determine 152 // readdir_r. For those and for links we use stat to determine
156 // the actual entry type. Notice that stat returns the type of 153 // the actual entry type. Notice that stat returns the type of
157 // the file pointed to. 154 // the file pointed to.
158 struct stat entry_info; 155 struct stat entry_info;
159 size_t written = snprintf(path + path_length, 156 if (!path->Add(entry.d_name)) {
160 PATH_MAX - path_length,
161 "%s",
162 entry.d_name);
163 if (written != strlen(entry.d_name)) {
164 success = false; 157 success = false;
165 break; 158 break;
166 } 159 }
167 int stat_success = TEMP_FAILURE_RETRY(stat(path, &entry_info)); 160 int stat_success = TEMP_FAILURE_RETRY(stat(path->data, &entry_info));
168 if (stat_success == -1) { 161 if (stat_success == -1) {
169 success = false; 162 success = false;
170 PostError(listing, path); 163 PostError(listing, path->data);
171 break; 164 break;
172 } 165 }
166 path->Reset(path_length);
173 if (S_ISDIR(entry_info.st_mode)) { 167 if (S_ISDIR(entry_info.st_mode)) {
174 success = HandleDir(entry.d_name, 168 success = HandleDir(entry.d_name,
175 path, 169 path,
176 path_length,
177 recursive, 170 recursive,
178 listing) && success; 171 listing) && success;
179 } else if (S_ISREG(entry_info.st_mode)) { 172 } else if (S_ISREG(entry_info.st_mode)) {
180 success = HandleFile(entry.d_name, 173 success = HandleFile(entry.d_name,
181 path, 174 path,
182 path_length,
183 listing) && success; 175 listing) && success;
184 } 176 }
185 ASSERT(!S_ISLNK(entry_info.st_mode)); 177 ASSERT(!S_ISLNK(entry_info.st_mode));
186 break; 178 break;
187 } 179 }
188 default: 180 default:
189 break; 181 break;
190 } 182 }
183 path->Reset(path_length);
191 } 184 }
192 185
193 if (read != 0) { 186 if (status != 0) {
194 errno = read; 187 errno = status;
195 success = false; 188 success = false;
196 PostError(listing, dir_name); 189 PostError(listing, dir_name);
197 } 190 }
198 191
199 if (closedir(dir_pointer) == -1) { 192 if (closedir(dir_pointer) == -1) {
200 success = false; 193 success = false;
201 PostError(listing, dir_name); 194 PostError(listing, dir_name);
202 } 195 }
203 free(path); 196 delete path;
204 197
205 return success; 198 return success;
206 } 199 }
207 200
208 201
209 static bool DeleteFile(char* file_name, 202 static bool DeleteFile(char* file_name,
210 char* path, 203 PathBuffer* path) {
211 int path_length) { 204 return path->Add(file_name) && remove(path->data) == 0;
212 size_t written = snprintf(path + path_length,
213 PATH_MAX - path_length,
214 "%s",
215 file_name);
216 if (written != strlen(file_name)) {
217 return false;
218 }
219 return (remove(path) == 0);
220 } 205 }
221 206
222 207
223 static bool DeleteDir(char* dir_name, 208 static bool DeleteDir(char* dir_name,
224 char* path, 209 PathBuffer* path) {
225 int path_length) { 210 if (strcmp(dir_name, ".") == 0) return true;
226 if (strcmp(dir_name, ".") != 0 && 211 if (strcmp(dir_name, "..") == 0) return true;
227 strcmp(dir_name, "..") != 0) { 212 return path->Add(dir_name) && DeleteRecursively(path->data);
228 size_t written = snprintf(path + path_length,
229 PATH_MAX - path_length,
230 "%s",
231 dir_name);
232 if (written != strlen(dir_name)) {
233 return false;
234 }
235 return DeleteRecursively(path);
236 }
237 return true;
238 } 213 }
239 214
240 215
241 static bool DeleteRecursively(const char* dir_name) { 216 static bool DeleteRecursively(const char* dir_name) {
242 // Do not recurse into links for deletion. Instead delete the link. 217 // Do not recurse into links for deletion. Instead delete the link.
243 struct stat st; 218 struct stat st;
244 if (TEMP_FAILURE_RETRY(lstat(dir_name, &st)) == -1) { 219 if (TEMP_FAILURE_RETRY(lstat(dir_name, &st)) == -1) {
245 return false; 220 return false;
246 } else if (S_ISLNK(st.st_mode)) { 221 } else if (S_ISLNK(st.st_mode)) {
247 return (remove(dir_name) == 0); 222 return (remove(dir_name) == 0);
248 } 223 }
249 224
250 // Not a link. Attempt to open as a directory and recurse into the 225 // Not a link. Attempt to open as a directory and recurse into the
251 // directory. 226 // directory.
252 DIR* dir_pointer; 227 DIR* dir_pointer;
253 do { 228 do {
254 dir_pointer = opendir(dir_name); 229 dir_pointer = opendir(dir_name);
255 } while (dir_pointer == NULL && errno == EINTR); 230 } while (dir_pointer == NULL && errno == EINTR);
256 231
257 if (dir_pointer == NULL) { 232 if (dir_pointer == NULL) {
258 return false; 233 return false;
259 } 234 }
260 235
261 // Compute full path for the directory currently being deleted. The 236 // Compute full path for the directory currently being deleted. The
262 // path buffer will be used to construct the current path in the 237 // path buffer will be used to construct the current path in the
263 // recursive traversal. path_length does not always equal 238 // recursive traversal.
264 // strlen(path) but indicates the current prefix of path that is the 239 PathBuffer* path = ComputeFullPath(dir_name);
265 // path of the current directory in the traversal. 240 if (path == NULL) return false;
266 char *path = static_cast<char*>(malloc(PATH_MAX));
267 ASSERT(path != NULL);
268 int path_length = 0;
269 bool valid = ComputeFullPath(dir_name, path, &path_length);
270 if (!valid) {
271 free(path);
272 return false;
273 }
274 241
275 // Iterate the directory and delete all files and directories. 242 // Iterate the directory and delete all files and directories.
243 int path_length = path->length;
276 int read = 0; 244 int read = 0;
277 bool success = true; 245 bool success = true;
278 dirent entry; 246 dirent entry;
279 dirent* result; 247 dirent* result;
280 while ((read = TEMP_FAILURE_RETRY(readdir_r(dir_pointer, 248 while ((read = TEMP_FAILURE_RETRY(readdir_r(dir_pointer,
281 &entry, 249 &entry,
282 &result))) == 0 && 250 &result))) == 0 &&
283 result != NULL && 251 result != NULL &&
284 success) { 252 success) {
285 switch (entry.d_type) { 253 switch (entry.d_type) {
286 case DT_DIR: 254 case DT_DIR:
287 success = success && DeleteDir(entry.d_name, path, path_length); 255 success = success && DeleteDir(entry.d_name, path);
288 break; 256 break;
289 case DT_REG: 257 case DT_REG:
290 case DT_LNK: 258 case DT_LNK:
291 // Treat all links as files. This will delete the link which 259 // Treat all links as files. This will delete the link which
292 // is what we want no matter if the link target is a file or a 260 // is what we want no matter if the link target is a file or a
293 // directory. 261 // directory.
294 success = success && DeleteFile(entry.d_name, path, path_length); 262 success = success && DeleteFile(entry.d_name, path);
295 break; 263 break;
296 case DT_UNKNOWN: { 264 case DT_UNKNOWN: {
297 // On some file systems the entry type is not determined by 265 // On some file systems the entry type is not determined by
298 // readdir_r. For those we use lstat to determine the entry 266 // readdir_r. For those we use lstat to determine the entry
299 // type. 267 // type.
300 struct stat entry_info; 268 struct stat entry_info;
301 size_t written = snprintf(path + path_length, 269 if (!path->Add(entry.d_name)) {
302 PATH_MAX - path_length,
303 "%s",
304 entry.d_name);
305 if (written != strlen(entry.d_name)) {
306 success = false; 270 success = false;
307 break; 271 break;
308 } 272 }
309 int lstat_success = TEMP_FAILURE_RETRY(lstat(path, &entry_info)); 273 int lstat_success = TEMP_FAILURE_RETRY(lstat(path->data, &entry_info));
310 if (lstat_success == -1) { 274 if (lstat_success == -1) {
311 success = false; 275 success = false;
312 break; 276 break;
313 } 277 }
278 path->Reset(path_length);
314 if (S_ISDIR(entry_info.st_mode)) { 279 if (S_ISDIR(entry_info.st_mode)) {
315 success = success && DeleteDir(entry.d_name, path, path_length); 280 success = success && DeleteDir(entry.d_name, path);
316 } else if (S_ISREG(entry_info.st_mode) || S_ISLNK(entry_info.st_mode)) { 281 } else if (S_ISREG(entry_info.st_mode) || S_ISLNK(entry_info.st_mode)) {
317 // Treat links as files. This will delete the link which is 282 // Treat links as files. This will delete the link which is
318 // what we want no matter if the link target is a file or a 283 // what we want no matter if the link target is a file or a
319 // directory. 284 // directory.
320 success = success && DeleteFile(entry.d_name, path, path_length); 285 success = success && DeleteFile(entry.d_name, path);
321 } 286 }
322 break; 287 break;
323 } 288 }
324 default: 289 default:
325 break; 290 break;
326 } 291 }
292 path->Reset(path_length);
327 } 293 }
328 294 delete path;
329 free(path);
330 295
331 if ((read != 0) || 296 if ((read != 0) ||
332 (closedir(dir_pointer) == -1) || 297 (closedir(dir_pointer) == -1) ||
333 (remove(dir_name) == -1)) { 298 (remove(dir_name) == -1)) {
334 return false; 299 return false;
335 } 300 }
336 301
337 return success; 302 return success;
338 } 303 }
339 304
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
390 } 355 }
391 return (result == 0); 356 return (result == 0);
392 } 357 }
393 358
394 359
395 char* Directory::CreateTemp(const char* const_template) { 360 char* Directory::CreateTemp(const char* const_template) {
396 // Returns a new, unused directory name, modifying the contents of 361 // Returns a new, unused directory name, modifying the contents of
397 // dir_template. Creates the directory with the permissions specified 362 // dir_template. Creates the directory with the permissions specified
398 // by the process umask. 363 // by the process umask.
399 // The return value must be freed by the caller. 364 // The return value must be freed by the caller.
400 char* path = static_cast<char*>(malloc(PATH_MAX + 1)); 365 PathBuffer* path = new PathBuffer();
401 SafeStrNCpy(path, const_template, PATH_MAX + 1); 366 path->Add(const_template);
402 int path_length = strlen(path); 367 if (path->length == 0) {
403 if (path_length > 0) { 368 path->Add("/tmp/temp_dir1_");
404 if ((path)[path_length - 1] == '/') { 369 } else if ((path->data)[path->length - 1] == '/') {
405 snprintf(path + path_length, PATH_MAX - path_length, "temp_dir_XXXXXX"); 370 path->Add("temp_dir_");
406 } else { 371 }
407 snprintf(path + path_length, PATH_MAX - path_length, "XXXXXX"); 372 if (!path->Add("XXXXXX")) {
408 } 373 // Pattern has overflowed.
409 } else { 374 delete path;
410 snprintf(path, PATH_MAX, "/tmp/temp_dir1_XXXXXX"); 375 return NULL;
411 } 376 }
412 char* result; 377 char* result;
413 do { 378 do {
414 result = mkdtemp(path); 379 result = mkdtemp(path->data);
415 } while (result == NULL && errno == EINTR); 380 } while (result == NULL && errno == EINTR);
416 if (result == NULL) { 381 if (result == NULL) {
417 free(path); 382 delete path;
418 return NULL; 383 return NULL;
419 } 384 }
420 return path; 385 int length = strnlen(path->data, PATH_MAX);
386 result = static_cast<char*>(malloc(length + 1));
387 strncpy(result, path->data, length);
388 result[length] = '\0';
389 delete path;
390 return result;
421 } 391 }
422 392
423 393
424 bool Directory::Delete(const char* dir_name, bool recursive) { 394 bool Directory::Delete(const char* dir_name, bool recursive) {
425 if (!recursive) { 395 if (!recursive) {
426 return (TEMP_FAILURE_RETRY(remove(dir_name)) == 0); 396 return (TEMP_FAILURE_RETRY(remove(dir_name)) == 0);
427 } else { 397 } else {
428 return DeleteRecursively(dir_name); 398 return DeleteRecursively(dir_name);
429 } 399 }
430 } 400 }
431 401
432 402
433 bool Directory::Rename(const char* path, const char* new_path) { 403 bool Directory::Rename(const char* path, const char* new_path) {
434 ExistsResult exists = Exists(path); 404 ExistsResult exists = Exists(path);
435 if (exists != EXISTS) return false; 405 if (exists != EXISTS) return false;
436 return (TEMP_FAILURE_RETRY(rename(path, new_path)) == 0); 406 return (TEMP_FAILURE_RETRY(rename(path, new_path)) == 0);
437 } 407 }
OLDNEW
« no previous file with comments | « runtime/bin/directory_android.cc ('k') | runtime/bin/directory_macos.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698