| 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 |