OLD | NEW |
1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "base/file_util.h" | 5 #include "base/file_util.h" |
6 | 6 |
7 #include <dirent.h> | 7 #include <dirent.h> |
8 #include <errno.h> | 8 #include <errno.h> |
9 #include <fcntl.h> | 9 #include <fcntl.h> |
10 #include <fnmatch.h> | 10 #include <fnmatch.h> |
11 #include <fts.h> | |
12 #include <libgen.h> | 11 #include <libgen.h> |
13 #include <stdio.h> | 12 #include <stdio.h> |
14 #include <string.h> | 13 #include <string.h> |
15 #include <sys/errno.h> | 14 #include <sys/errno.h> |
16 #include <sys/mman.h> | 15 #include <sys/mman.h> |
17 #include <sys/stat.h> | 16 #include <sys/stat.h> |
18 #include <sys/types.h> | 17 #include <sys/types.h> |
19 #include <time.h> | 18 #include <time.h> |
20 #include <unistd.h> | 19 #include <unistd.h> |
21 | 20 |
22 #include <fstream> | 21 #include <fstream> |
23 | 22 |
24 #include "base/basictypes.h" | 23 #include "base/basictypes.h" |
25 #include "base/eintr_wrapper.h" | 24 #include "base/eintr_wrapper.h" |
26 #include "base/file_path.h" | 25 #include "base/file_path.h" |
27 #include "base/lock.h" | 26 #include "base/lock.h" |
28 #include "base/logging.h" | 27 #include "base/logging.h" |
29 #include "base/scoped_ptr.h" | 28 #include "base/scoped_ptr.h" |
30 #include "base/singleton.h" | 29 #include "base/singleton.h" |
31 #include "base/string_util.h" | 30 #include "base/string_util.h" |
32 #include "base/sys_string_conversions.h" | 31 #include "base/sys_string_conversions.h" |
33 #include "base/time.h" | 32 #include "base/time.h" |
34 #include "unicode/coll.h" | 33 #include "unicode/coll.h" |
35 | 34 |
36 namespace { | 35 namespace { |
37 | 36 |
38 bool IsDirectory(const FTSENT* file) { | |
39 switch (file->fts_info) { | |
40 case FTS_D: | |
41 case FTS_DC: | |
42 case FTS_DNR: | |
43 case FTS_DOT: | |
44 case FTS_DP: | |
45 return true; | |
46 default: | |
47 return false; | |
48 } | |
49 } | |
50 | |
51 class LocaleAwareComparator { | 37 class LocaleAwareComparator { |
52 public: | 38 public: |
53 LocaleAwareComparator() { | 39 LocaleAwareComparator() { |
54 UErrorCode error_code = U_ZERO_ERROR; | 40 UErrorCode error_code = U_ZERO_ERROR; |
55 // Use the default collator. The default locale should have been properly | 41 // Use the default collator. The default locale should have been properly |
56 // set by the time this constructor is called. | 42 // set by the time this constructor is called. |
57 collator_.reset(Collator::createInstance(error_code)); | 43 collator_.reset(Collator::createInstance(error_code)); |
58 DCHECK(U_SUCCESS(error_code)); | 44 DCHECK(U_SUCCESS(error_code)); |
59 // Make it case-sensitive. | 45 // Make it case-sensitive. |
60 collator_->setStrength(Collator::TERTIARY); | 46 collator_->setStrength(Collator::TERTIARY); |
(...skipping 24 matching lines...) Expand all Loading... |
85 } | 71 } |
86 | 72 |
87 private: | 73 private: |
88 scoped_ptr<Collator> collator_; | 74 scoped_ptr<Collator> collator_; |
89 Lock lock_; | 75 Lock lock_; |
90 friend struct DefaultSingletonTraits<LocaleAwareComparator>; | 76 friend struct DefaultSingletonTraits<LocaleAwareComparator>; |
91 | 77 |
92 DISALLOW_COPY_AND_ASSIGN(LocaleAwareComparator); | 78 DISALLOW_COPY_AND_ASSIGN(LocaleAwareComparator); |
93 }; | 79 }; |
94 | 80 |
95 int CompareFiles(const FTSENT** a, const FTSENT** b) { | |
96 // Order lexicographically with directories before other files. | |
97 const bool a_is_dir = IsDirectory(*a); | |
98 const bool b_is_dir = IsDirectory(*b); | |
99 if (a_is_dir != b_is_dir) | |
100 return a_is_dir ? -1 : 1; | |
101 | |
102 // On linux, the file system encoding is not defined. We assume | |
103 // SysNativeMBToWide takes care of it. | |
104 // | |
105 // ICU's collator can take strings in OS native encoding. But we convert the | |
106 // strings to UTF-16 ourselves to ensure conversion consistency. | |
107 // TODO(yuzo): Perhaps we should define SysNativeMBToUTF16? | |
108 return Singleton<LocaleAwareComparator>()->Compare( | |
109 WideToUTF16(base::SysNativeMBToWide((*a)->fts_name)), | |
110 WideToUTF16(base::SysNativeMBToWide((*b)->fts_name))); | |
111 } | |
112 | |
113 } // namespace | 81 } // namespace |
114 | 82 |
115 namespace file_util { | 83 namespace file_util { |
116 | 84 |
117 #if defined(GOOGLE_CHROME_BUILD) | 85 #if defined(GOOGLE_CHROME_BUILD) |
118 static const char* kTempFileName = "com.google.chrome.XXXXXX"; | 86 static const char* kTempFileName = "com.google.chrome.XXXXXX"; |
119 #else | 87 #else |
120 static const char* kTempFileName = "org.chromium.XXXXXX"; | 88 static const char* kTempFileName = "org.chromium.XXXXXX"; |
121 #endif | 89 #endif |
122 | 90 |
(...skipping 16 matching lines...) Expand all Loading... |
139 *path = FilePath(full_path); | 107 *path = FilePath(full_path); |
140 return true; | 108 return true; |
141 } | 109 } |
142 | 110 |
143 int CountFilesCreatedAfter(const FilePath& path, | 111 int CountFilesCreatedAfter(const FilePath& path, |
144 const base::Time& comparison_time) { | 112 const base::Time& comparison_time) { |
145 int file_count = 0; | 113 int file_count = 0; |
146 | 114 |
147 DIR* dir = opendir(path.value().c_str()); | 115 DIR* dir = opendir(path.value().c_str()); |
148 if (dir) { | 116 if (dir) { |
| 117 #if !defined(OS_LINUX) && !defined(OS_MACOSX) |
| 118 #error Depending on the definition of struct dirent, additional space for \ |
| 119 pathname may be needed |
| 120 #endif |
149 struct dirent ent_buf; | 121 struct dirent ent_buf; |
150 struct dirent* ent; | 122 struct dirent* ent; |
151 while (readdir_r(dir, &ent_buf, &ent) == 0 && ent) { | 123 while (readdir_r(dir, &ent_buf, &ent) == 0 && ent) { |
152 if ((strcmp(ent->d_name, ".") == 0) || | 124 if ((strcmp(ent->d_name, ".") == 0) || |
153 (strcmp(ent->d_name, "..") == 0)) | 125 (strcmp(ent->d_name, "..") == 0)) |
154 continue; | 126 continue; |
155 | 127 |
156 struct stat64 st; | 128 struct stat64 st; |
157 int test = stat64(path.Append(ent->d_name).value().c_str(), &st); | 129 int test = stat64(path.Append(ent->d_name).value().c_str(), &st); |
158 if (test != 0) { | 130 if (test != 0) { |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
194 // The Windows version defines this condition as success. | 166 // The Windows version defines this condition as success. |
195 bool ret = (errno == ENOENT || errno == ENOTDIR); | 167 bool ret = (errno == ENOENT || errno == ENOTDIR); |
196 return ret; | 168 return ret; |
197 } | 169 } |
198 if (!S_ISDIR(file_info.st_mode)) | 170 if (!S_ISDIR(file_info.st_mode)) |
199 return (unlink(path_str) == 0); | 171 return (unlink(path_str) == 0); |
200 if (!recursive) | 172 if (!recursive) |
201 return (rmdir(path_str) == 0); | 173 return (rmdir(path_str) == 0); |
202 | 174 |
203 bool success = true; | 175 bool success = true; |
204 int ftsflags = FTS_PHYSICAL | FTS_NOSTAT; | 176 std::stack<std::string> directories; |
205 char top_dir[PATH_MAX]; | 177 directories.push(path.value()); |
206 if (base::strlcpy(top_dir, path_str, | 178 FileEnumerator traversal(path, true, static_cast<FileEnumerator::FILE_TYPE>( |
207 arraysize(top_dir)) >= arraysize(top_dir)) { | 179 FileEnumerator::FILES | FileEnumerator::DIRECTORIES | |
208 return false; | 180 FileEnumerator::SHOW_SYM_LINKS)); |
| 181 for (FilePath current = traversal.Next(); success && !current.empty(); |
| 182 current = traversal.Next()) { |
| 183 FileEnumerator::FindInfo info; |
| 184 traversal.GetFindInfo(&info); |
| 185 |
| 186 if (S_ISDIR(info.stat.st_mode)) |
| 187 directories.push(current.value()); |
| 188 else |
| 189 success = (unlink(current.value().c_str()) == 0); |
209 } | 190 } |
210 char* dir_list[2] = { top_dir, NULL }; | 191 |
211 FTS* fts = fts_open(dir_list, ftsflags, NULL); | 192 while (success && !directories.empty()) { |
212 if (fts) { | 193 FilePath dir = FilePath(directories.top()); |
213 FTSENT* fts_ent = fts_read(fts); | 194 directories.pop(); |
214 while (success && fts_ent != NULL) { | 195 success = (rmdir(dir.value().c_str()) == 0); |
215 switch (fts_ent->fts_info) { | |
216 case FTS_DNR: | |
217 case FTS_ERR: | |
218 // log error | |
219 success = false; | |
220 continue; | |
221 break; | |
222 case FTS_DP: | |
223 success = (rmdir(fts_ent->fts_accpath) == 0); | |
224 break; | |
225 case FTS_D: | |
226 break; | |
227 case FTS_NSOK: | |
228 case FTS_F: | |
229 case FTS_SL: | |
230 case FTS_SLNONE: | |
231 success = (unlink(fts_ent->fts_accpath) == 0); | |
232 break; | |
233 default: | |
234 DCHECK(false); | |
235 break; | |
236 } | |
237 fts_ent = fts_read(fts); | |
238 } | |
239 fts_close(fts); | |
240 } | 196 } |
| 197 |
241 return success; | 198 return success; |
242 } | 199 } |
243 | 200 |
244 bool Move(const FilePath& from_path, const FilePath& to_path) { | 201 bool Move(const FilePath& from_path, const FilePath& to_path) { |
245 if (rename(from_path.value().c_str(), to_path.value().c_str()) == 0) | 202 if (rename(from_path.value().c_str(), to_path.value().c_str()) == 0) |
246 return true; | 203 return true; |
247 | 204 |
248 if (!CopyDirectory(from_path, to_path, true)) | 205 if (!CopyDirectory(from_path, to_path, true)) |
249 return false; | 206 return false; |
250 | 207 |
(...skipping 14 matching lines...) Expand all Loading... |
265 // TODO(evanm): remove this once we're sure it's ok. | 222 // TODO(evanm): remove this once we're sure it's ok. |
266 DCHECK(to_path.value().find('*') == std::string::npos); | 223 DCHECK(to_path.value().find('*') == std::string::npos); |
267 DCHECK(from_path.value().find('*') == std::string::npos); | 224 DCHECK(from_path.value().find('*') == std::string::npos); |
268 | 225 |
269 char top_dir[PATH_MAX]; | 226 char top_dir[PATH_MAX]; |
270 if (base::strlcpy(top_dir, from_path.value().c_str(), | 227 if (base::strlcpy(top_dir, from_path.value().c_str(), |
271 arraysize(top_dir)) >= arraysize(top_dir)) { | 228 arraysize(top_dir)) >= arraysize(top_dir)) { |
272 return false; | 229 return false; |
273 } | 230 } |
274 | 231 |
275 char* dir_list[] = { top_dir, NULL }; | 232 // This function does not properly handle destinations within the source |
276 FTS* fts = fts_open(dir_list, FTS_PHYSICAL | FTS_NOSTAT, NULL); | 233 FilePath real_to_path = to_path; |
277 if (!fts) { | 234 if (PathExists(real_to_path)) { |
278 LOG(ERROR) << "fts_open failed: " << strerror(errno); | 235 if (!AbsolutePath(&real_to_path)) |
| 236 return false; |
| 237 } else { |
| 238 real_to_path = real_to_path.DirName(); |
| 239 if (!AbsolutePath(&real_to_path)) |
| 240 return false; |
| 241 } |
| 242 FilePath real_from_path = from_path; |
| 243 if (!AbsolutePath(&real_from_path)) |
279 return false; | 244 return false; |
| 245 if (real_to_path.value().size() >= real_from_path.value().size() && |
| 246 real_to_path.value().compare(0, real_from_path.value().size(), |
| 247 real_from_path.value()) == 0) |
| 248 return false; |
| 249 |
| 250 bool success = true; |
| 251 FileEnumerator::FILE_TYPE traverse_type = |
| 252 static_cast<FileEnumerator::FILE_TYPE>(FileEnumerator::FILES | |
| 253 FileEnumerator::SHOW_SYM_LINKS); |
| 254 if (recursive) |
| 255 traverse_type = static_cast<FileEnumerator::FILE_TYPE>( |
| 256 traverse_type | FileEnumerator::DIRECTORIES); |
| 257 FileEnumerator traversal(from_path, recursive, traverse_type); |
| 258 |
| 259 // to_path may not exist yet, start the loop with to_path |
| 260 FileEnumerator::FindInfo info; |
| 261 FilePath current = from_path; |
| 262 if (stat(from_path.value().c_str(), &info.stat) < 0) { |
| 263 LOG(ERROR) << "CopyDirectory() couldn't stat source directory: " << |
| 264 from_path.value() << " errno = " << errno; |
| 265 success = false; |
280 } | 266 } |
281 | 267 |
282 int error = 0; | 268 while (success && !current.empty()) { |
283 FTSENT* ent; | 269 // current is the source path, including from_path, so paste |
284 while (!error && (ent = fts_read(fts)) != NULL) { | |
285 // ent->fts_path is the source path, including from_path, so paste | |
286 // the suffix after from_path onto to_path to create the target_path. | 270 // the suffix after from_path onto to_path to create the target_path. |
287 std::string suffix(&ent->fts_path[from_path.value().size()]); | 271 std::string suffix(¤t.value().c_str()[from_path.value().size()]); |
288 // Strip the leading '/' (if any). | 272 // Strip the leading '/' (if any). |
289 if (!suffix.empty()) { | 273 if (!suffix.empty()) { |
290 DCHECK_EQ('/', suffix[0]); | 274 DCHECK_EQ('/', suffix[0]); |
291 suffix.erase(0, 1); | 275 suffix.erase(0, 1); |
292 } | 276 } |
293 const FilePath target_path = to_path.Append(suffix); | 277 const FilePath target_path = to_path.Append(suffix); |
294 switch (ent->fts_info) { | |
295 case FTS_D: // Preorder directory. | |
296 // If we encounter a subdirectory in a non-recursive copy, prune it | |
297 // from the traversal. | |
298 if (!recursive && ent->fts_level > 0) { | |
299 if (fts_set(fts, ent, FTS_SKIP) != 0) | |
300 error = errno; | |
301 continue; | |
302 } | |
303 | 278 |
304 // Try creating the target dir, continuing on it if it exists already. | 279 if (S_ISDIR(info.stat.st_mode)) { |
305 if (mkdir(target_path.value().c_str(), 0700) != 0) { | 280 if (mkdir(target_path.value().c_str(), info.stat.st_mode & 01777) != 0 && |
306 if (errno != EEXIST) | 281 errno != EEXIST) { |
307 error = errno; | 282 LOG(ERROR) << "CopyDirectory() couldn't create directory: " << |
308 } | 283 target_path.value() << " errno = " << errno; |
309 break; | 284 success = false; |
310 case FTS_F: // Regular file. | 285 } |
311 case FTS_NSOK: // File, no stat info requested. | 286 } else if (S_ISREG(info.stat.st_mode)) { |
312 errno = 0; | 287 if (!CopyFile(current, target_path)) { |
313 if (!CopyFile(FilePath(ent->fts_path), target_path)) | 288 LOG(ERROR) << "CopyDirectory() couldn't create file: " << |
314 error = errno ? errno : EINVAL; | 289 target_path.value(); |
315 break; | 290 success = false; |
316 case FTS_DP: // Postorder directory. | 291 } |
317 case FTS_DOT: // "." or ".." | 292 } else { |
318 // Skip it. | 293 LOG(WARNING) << "CopyDirectory() skipping non-regular file: " << |
319 continue; | 294 current.value(); |
320 case FTS_DC: // Directory causing a cycle. | |
321 // Skip this branch. | |
322 if (fts_set(fts, ent, FTS_SKIP) != 0) | |
323 error = errno; | |
324 break; | |
325 case FTS_DNR: // Directory cannot be read. | |
326 case FTS_ERR: // Error. | |
327 case FTS_NS: // Stat failed. | |
328 // Abort with the error. | |
329 error = ent->fts_errno; | |
330 break; | |
331 case FTS_SL: // Symlink. | |
332 case FTS_SLNONE: // Symlink with broken target. | |
333 LOG(WARNING) << "CopyDirectory() skipping symbolic link: " << | |
334 ent->fts_path; | |
335 continue; | |
336 case FTS_DEFAULT: // Some other sort of file. | |
337 LOG(WARNING) << "CopyDirectory() skipping file of unknown type: " << | |
338 ent->fts_path; | |
339 continue; | |
340 default: | |
341 NOTREACHED(); | |
342 continue; // Hope for the best! | |
343 } | 295 } |
344 } | |
345 // fts_read may have returned NULL and set errno to indicate an error. | |
346 if (!error && errno != 0) | |
347 error = errno; | |
348 | 296 |
349 if (!fts_close(fts)) { | 297 current = traversal.Next(); |
350 // If we already have an error, let's use that error instead of the error | 298 traversal.GetFindInfo(&info); |
351 // fts_close set. | |
352 if (!error) | |
353 error = errno; | |
354 } | 299 } |
355 | 300 |
356 if (error) { | 301 return success; |
357 LOG(ERROR) << "CopyDirectory(): " << strerror(error); | |
358 return false; | |
359 } | |
360 return true; | |
361 } | 302 } |
362 | 303 |
363 bool PathExists(const FilePath& path) { | 304 bool PathExists(const FilePath& path) { |
364 struct stat64 file_info; | 305 struct stat64 file_info; |
365 return (stat64(path.value().c_str(), &file_info) == 0); | 306 return (stat64(path.value().c_str(), &file_info) == 0); |
366 } | 307 } |
367 | 308 |
368 bool PathIsWritable(const FilePath& path) { | 309 bool PathIsWritable(const FilePath& path) { |
369 FilePath test_path(path); | 310 FilePath test_path(path); |
370 struct stat64 file_info; | 311 struct stat64 file_info; |
(...skipping 224 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
595 int ret = chdir(path.value().c_str()); | 536 int ret = chdir(path.value().c_str()); |
596 return !ret; | 537 return !ret; |
597 } | 538 } |
598 | 539 |
599 /////////////////////////////////////////////// | 540 /////////////////////////////////////////////// |
600 // FileEnumerator | 541 // FileEnumerator |
601 | 542 |
602 FileEnumerator::FileEnumerator(const FilePath& root_path, | 543 FileEnumerator::FileEnumerator(const FilePath& root_path, |
603 bool recursive, | 544 bool recursive, |
604 FileEnumerator::FILE_TYPE file_type) | 545 FileEnumerator::FILE_TYPE file_type) |
605 : recursive_(recursive), | 546 : root_path_(root_path), |
| 547 recursive_(recursive), |
606 file_type_(file_type), | 548 file_type_(file_type), |
607 is_in_find_op_(false), | 549 is_in_find_op_(false), |
608 fts_(NULL) { | 550 current_directory_entry_(0) { |
609 // INCLUDE_DOT_DOT must not be specified if recursive. | 551 // INCLUDE_DOT_DOT must not be specified if recursive. |
610 DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_))); | 552 DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_))); |
611 pending_paths_.push(root_path); | 553 pending_paths_.push(root_path); |
612 } | 554 } |
613 | 555 |
614 FileEnumerator::FileEnumerator(const FilePath& root_path, | 556 FileEnumerator::FileEnumerator(const FilePath& root_path, |
615 bool recursive, | 557 bool recursive, |
616 FileEnumerator::FILE_TYPE file_type, | 558 FileEnumerator::FILE_TYPE file_type, |
617 const FilePath::StringType& pattern) | 559 const FilePath::StringType& pattern) |
618 : recursive_(recursive), | 560 : root_path_(root_path), |
| 561 recursive_(recursive), |
619 file_type_(file_type), | 562 file_type_(file_type), |
620 pattern_(root_path.value()), | 563 pattern_(root_path.Append(pattern)), |
621 is_in_find_op_(false), | 564 is_in_find_op_(false), |
622 fts_(NULL) { | 565 current_directory_entry_(0) { |
623 // INCLUDE_DOT_DOT must not be specified if recursive. | 566 // INCLUDE_DOT_DOT must not be specified if recursive. |
624 DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_))); | 567 DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_))); |
625 // The Windows version of this code only matches against items in the top-most | 568 // The Windows version of this code appends the pattern to the root_path, |
626 // directory, and we're comparing fnmatch against full paths, so this is the | 569 // potentially only matching against items in the top-most directory. |
627 // easiest way to get the right pattern. | 570 // Do the same here. |
628 pattern_ = pattern_.Append(pattern); | 571 if (pattern.size() == 0) |
| 572 pattern_ = FilePath(); |
629 pending_paths_.push(root_path); | 573 pending_paths_.push(root_path); |
630 } | 574 } |
631 | 575 |
632 FileEnumerator::~FileEnumerator() { | 576 FileEnumerator::~FileEnumerator() { |
633 if (fts_) | |
634 fts_close(fts_); | |
635 } | 577 } |
636 | 578 |
637 void FileEnumerator::GetFindInfo(FindInfo* info) { | 579 void FileEnumerator::GetFindInfo(FindInfo* info) { |
638 DCHECK(info); | 580 DCHECK(info); |
639 | 581 |
640 if (!is_in_find_op_) | 582 if (current_directory_entry_ >= directory_entries_.size()) |
641 return; | 583 return; |
642 | 584 |
643 memcpy(&(info->stat), fts_ent_->fts_statp, sizeof(info->stat)); | 585 DirectoryEntryInfo* cur_entry = &directory_entries_[current_directory_entry_]; |
644 info->filename.assign(fts_ent_->fts_name); | 586 memcpy(&(info->stat), &(cur_entry->stat), sizeof(info->stat)); |
| 587 info->filename.assign(cur_entry->filename.value()); |
645 } | 588 } |
646 | 589 |
647 // As it stands, this method calls itself recursively when the next item of | |
648 // the fts enumeration doesn't match (type, pattern, etc.). In the case of | |
649 // large directories with many files this can be quite deep. | |
650 // TODO(erikkay) - get rid of this recursive pattern | |
651 FilePath FileEnumerator::Next() { | 590 FilePath FileEnumerator::Next() { |
652 if (!is_in_find_op_) { | 591 ++current_directory_entry_; |
| 592 |
| 593 // While we've exhausted the entries in the current directory, do the next |
| 594 while (current_directory_entry_ >= directory_entries_.size()) { |
653 if (pending_paths_.empty()) | 595 if (pending_paths_.empty()) |
654 return FilePath(); | 596 return FilePath(); |
655 | 597 |
656 // The last find FindFirstFile operation is done, prepare a new one. | |
657 root_path_ = pending_paths_.top(); | 598 root_path_ = pending_paths_.top(); |
658 root_path_ = root_path_.StripTrailingSeparators(); | 599 root_path_ = root_path_.StripTrailingSeparators(); |
659 pending_paths_.pop(); | 600 pending_paths_.pop(); |
660 | 601 |
661 // Start a new find operation. | 602 std::vector<DirectoryEntryInfo> entries; |
662 int ftsflags = FTS_LOGICAL | FTS_SEEDOT; | 603 if (!ReadDirectory(&entries, root_path_, file_type_ & SHOW_SYM_LINKS)) |
663 char top_dir[PATH_MAX]; | 604 continue; |
664 base::strlcpy(top_dir, root_path_.value().c_str(), arraysize(top_dir)); | |
665 char* dir_list[2] = { top_dir, NULL }; | |
666 fts_ = fts_open(dir_list, ftsflags, CompareFiles); | |
667 if (!fts_) | |
668 return Next(); | |
669 is_in_find_op_ = true; | |
670 } | |
671 | 605 |
672 fts_ent_ = fts_read(fts_); | 606 // The API says that order is not guaranteed, but order affects UX |
673 if (fts_ent_ == NULL) { | 607 std::sort(entries.begin(), entries.end(), CompareFiles); |
674 fts_close(fts_); | |
675 fts_ = NULL; | |
676 is_in_find_op_ = false; | |
677 return Next(); | |
678 } | |
679 | 608 |
680 // Level 0 is the top, which is always skipped. | 609 directory_entries_.clear(); |
681 if (fts_ent_->fts_level == 0) | 610 current_directory_entry_ = 0; |
682 return Next(); | 611 for (std::vector<DirectoryEntryInfo>::const_iterator |
| 612 i = entries.begin(); i != entries.end(); ++i) { |
| 613 FilePath full_path = root_path_.Append(i->filename); |
| 614 if (ShouldSkip(full_path)) |
| 615 continue; |
683 | 616 |
684 // Patterns are only matched on the items in the top-most directory. | 617 if (pattern_.value().size() && |
685 // (see Windows implementation) | 618 fnmatch(pattern_.value().c_str(), full_path.value().c_str(), |
686 if (fts_ent_->fts_level == 1 && pattern_.value().length() > 0) { | 619 FNM_NOESCAPE)) |
687 if (fnmatch(pattern_.value().c_str(), fts_ent_->fts_path, 0) != 0) { | 620 continue; |
688 if (fts_ent_->fts_info == FTS_D) | 621 |
689 fts_set(fts_, fts_ent_, FTS_SKIP); | 622 if (recursive_ && S_ISDIR(i->stat.st_mode)) |
690 return Next(); | 623 pending_paths_.push(full_path); |
| 624 |
| 625 if ((S_ISDIR(i->stat.st_mode) && (file_type_ & DIRECTORIES)) || |
| 626 (!S_ISDIR(i->stat.st_mode) && (file_type_ & FILES))) |
| 627 directory_entries_.push_back(*i); |
691 } | 628 } |
692 } | 629 } |
693 | 630 |
694 FilePath cur_file(fts_ent_->fts_path); | 631 return root_path_.Append(directory_entries_[current_directory_entry_ |
695 if (ShouldSkip(cur_file)) | 632 ].filename); |
696 return Next(); | 633 } |
697 | 634 |
698 if (fts_ent_->fts_info == FTS_D) { | 635 bool FileEnumerator::ReadDirectory(std::vector<DirectoryEntryInfo>* entries, |
699 // If not recursive, then prune children. | 636 const FilePath& source, bool show_links) { |
700 if (!recursive_) | 637 DIR* dir = opendir(source.value().c_str()); |
701 fts_set(fts_, fts_ent_, FTS_SKIP); | 638 if (!dir) |
702 return (file_type_ & FileEnumerator::DIRECTORIES) ? cur_file : Next(); | 639 return false; |
703 } else if (fts_ent_->fts_info == FTS_F) { | 640 |
704 return (file_type_ & FileEnumerator::FILES) ? cur_file : Next(); | 641 #if !defined(OS_LINUX) && !defined(OS_MACOSX) |
705 } else if (fts_ent_->fts_info == FTS_DOT) { | 642 #error Depending on the definition of struct dirent, additional space for \ |
706 if ((file_type_ & FileEnumerator::DIRECTORIES) && IsDotDot(cur_file)) { | 643 pathname may be needed |
707 return cur_file; | 644 #endif |
| 645 struct dirent dent_buf; |
| 646 struct dirent* dent; |
| 647 while (readdir_r(dir, &dent_buf, &dent) == 0 && dent) { |
| 648 DirectoryEntryInfo info; |
| 649 FilePath full_name; |
| 650 int stat_value; |
| 651 |
| 652 info.filename = FilePath(dent->d_name); |
| 653 full_name = source.Append(dent->d_name); |
| 654 if (show_links) |
| 655 stat_value = lstat(full_name.value().c_str(), &info.stat); |
| 656 else |
| 657 stat_value = stat(full_name.value().c_str(), &info.stat); |
| 658 if (stat_value < 0) { |
| 659 LOG(ERROR) << "Couldn't stat file: " << |
| 660 source.Append(dent->d_name).value().c_str() << " errno = " << errno; |
| 661 memset(&info.stat, 0, sizeof(info.stat)); |
708 } | 662 } |
709 return Next(); | 663 entries->push_back(info); |
710 } | 664 } |
711 // TODO(erikkay) - verify that the other fts_info types aren't interesting | 665 |
712 return Next(); | 666 closedir(dir); |
| 667 return true; |
| 668 } |
| 669 |
| 670 bool FileEnumerator::CompareFiles(const DirectoryEntryInfo& a, |
| 671 const DirectoryEntryInfo& b) { |
| 672 // Order lexicographically with directories before other files. |
| 673 if (S_ISDIR(a.stat.st_mode) != S_ISDIR(b.stat.st_mode)) |
| 674 return S_ISDIR(a.stat.st_mode); |
| 675 |
| 676 // On linux, the file system encoding is not defined. We assume |
| 677 // SysNativeMBToWide takes care of it. |
| 678 // |
| 679 // ICU's collator can take strings in OS native encoding. But we convert the |
| 680 // strings to UTF-16 ourselves to ensure conversion consistency. |
| 681 // TODO(yuzo): Perhaps we should define SysNativeMBToUTF16? |
| 682 return Singleton<LocaleAwareComparator>()->Compare( |
| 683 WideToUTF16(base::SysNativeMBToWide(a.filename.value().c_str())), |
| 684 WideToUTF16(base::SysNativeMBToWide(b.filename.value().c_str()))) < 0; |
713 } | 685 } |
714 | 686 |
715 /////////////////////////////////////////////// | 687 /////////////////////////////////////////////// |
716 // MemoryMappedFile | 688 // MemoryMappedFile |
717 | 689 |
718 MemoryMappedFile::MemoryMappedFile() | 690 MemoryMappedFile::MemoryMappedFile() |
719 : file_(-1), | 691 : file_(-1), |
720 data_(NULL), | 692 data_(NULL), |
721 length_(0) { | 693 length_(0) { |
722 } | 694 } |
(...skipping 26 matching lines...) Expand all Loading... |
749 munmap(data_, length_); | 721 munmap(data_, length_); |
750 if (file_ != -1) | 722 if (file_ != -1) |
751 close(file_); | 723 close(file_); |
752 | 724 |
753 data_ = NULL; | 725 data_ = NULL; |
754 length_ = 0; | 726 length_ = 0; |
755 file_ = -1; | 727 file_ = -1; |
756 } | 728 } |
757 | 729 |
758 } // namespace file_util | 730 } // namespace file_util |
OLD | NEW |