OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 <libgen.h> | 11 #include <libgen.h> |
11 #include <limits.h> | 12 #include <limits.h> |
12 #include <stdio.h> | 13 #include <stdio.h> |
13 #include <stdlib.h> | 14 #include <stdlib.h> |
14 #include <string.h> | 15 #include <string.h> |
15 #include <sys/errno.h> | 16 #include <sys/errno.h> |
16 #include <sys/mman.h> | 17 #include <sys/mman.h> |
17 #include <sys/param.h> | 18 #include <sys/param.h> |
18 #include <sys/stat.h> | 19 #include <sys/stat.h> |
19 #include <sys/time.h> | 20 #include <sys/time.h> |
20 #include <sys/types.h> | 21 #include <sys/types.h> |
21 #include <time.h> | 22 #include <time.h> |
22 #include <unistd.h> | 23 #include <unistd.h> |
23 | 24 |
24 #if defined(OS_MACOSX) | 25 #if defined(OS_MACOSX) |
25 #include <AvailabilityMacros.h> | 26 #include <AvailabilityMacros.h> |
26 #include "base/mac/foundation_util.h" | 27 #include "base/mac/foundation_util.h" |
27 #elif !defined(OS_ANDROID) | 28 #elif !defined(OS_ANDROID) |
28 #include <glib.h> | 29 #include <glib.h> |
29 #endif | 30 #endif |
30 | 31 |
31 #include <fstream> | 32 #include <fstream> |
32 | 33 |
33 #include "base/basictypes.h" | 34 #include "base/basictypes.h" |
34 #include "base/files/file_enumerator.h" | |
35 #include "base/files/file_path.h" | 35 #include "base/files/file_path.h" |
36 #include "base/logging.h" | 36 #include "base/logging.h" |
37 #include "base/memory/scoped_ptr.h" | 37 #include "base/memory/scoped_ptr.h" |
38 #include "base/memory/singleton.h" | 38 #include "base/memory/singleton.h" |
39 #include "base/path_service.h" | 39 #include "base/path_service.h" |
40 #include "base/posix/eintr_wrapper.h" | 40 #include "base/posix/eintr_wrapper.h" |
41 #include "base/stl_util.h" | 41 #include "base/stl_util.h" |
42 #include "base/string_util.h" | 42 #include "base/string_util.h" |
43 #include "base/stringprintf.h" | 43 #include "base/stringprintf.h" |
44 #include "base/strings/sys_string_conversions.h" | 44 #include "base/strings/sys_string_conversions.h" |
45 #include "base/threading/thread_restrictions.h" | 45 #include "base/threading/thread_restrictions.h" |
46 #include "base/time.h" | 46 #include "base/time.h" |
47 #include "base/utf_string_conversions.h" | 47 #include "base/utf_string_conversions.h" |
48 | 48 |
49 #if defined(OS_ANDROID) | 49 #if defined(OS_ANDROID) |
50 #include "base/os_compat_android.h" | 50 #include "base/os_compat_android.h" |
51 #endif | 51 #endif |
52 | 52 |
53 #if !defined(OS_IOS) | 53 #if !defined(OS_IOS) |
54 #include <grp.h> | 54 #include <grp.h> |
55 #endif | 55 #endif |
56 | 56 |
57 #if defined(OS_CHROMEOS) | 57 #if defined(OS_CHROMEOS) |
58 #include "base/chromeos/chromeos_version.h" | 58 #include "base/chromeos/chromeos_version.h" |
59 #endif | 59 #endif |
60 | 60 |
61 using base::FileEnumerator; | |
62 using base::FilePath; | 61 using base::FilePath; |
63 using base::MakeAbsoluteFilePath; | 62 using base::MakeAbsoluteFilePath; |
64 | 63 |
65 namespace base { | 64 namespace base { |
66 | 65 |
67 FilePath MakeAbsoluteFilePath(const FilePath& input) { | 66 FilePath MakeAbsoluteFilePath(const FilePath& input) { |
68 base::ThreadRestrictions::AssertIOAllowed(); | 67 base::ThreadRestrictions::AssertIOAllowed(); |
69 char full_path[PATH_MAX]; | 68 char full_path[PATH_MAX]; |
70 if (realpath(input.value().c_str(), full_path) == NULL) | 69 if (realpath(input.value().c_str(), full_path) == NULL) |
71 return FilePath(); | 70 return FilePath(); |
(...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
184 return (rmdir(path_str) == 0); | 183 return (rmdir(path_str) == 0); |
185 | 184 |
186 bool success = true; | 185 bool success = true; |
187 std::stack<std::string> directories; | 186 std::stack<std::string> directories; |
188 directories.push(path.value()); | 187 directories.push(path.value()); |
189 FileEnumerator traversal(path, true, | 188 FileEnumerator traversal(path, true, |
190 FileEnumerator::FILES | FileEnumerator::DIRECTORIES | | 189 FileEnumerator::FILES | FileEnumerator::DIRECTORIES | |
191 FileEnumerator::SHOW_SYM_LINKS); | 190 FileEnumerator::SHOW_SYM_LINKS); |
192 for (FilePath current = traversal.Next(); success && !current.empty(); | 191 for (FilePath current = traversal.Next(); success && !current.empty(); |
193 current = traversal.Next()) { | 192 current = traversal.Next()) { |
194 if (traversal.GetInfo().IsDirectory()) | 193 FileEnumerator::FindInfo info; |
| 194 traversal.GetFindInfo(&info); |
| 195 |
| 196 if (S_ISDIR(info.stat.st_mode)) |
195 directories.push(current.value()); | 197 directories.push(current.value()); |
196 else | 198 else |
197 success = (unlink(current.value().c_str()) == 0); | 199 success = (unlink(current.value().c_str()) == 0); |
198 } | 200 } |
199 | 201 |
200 while (success && !directories.empty()) { | 202 while (success && !directories.empty()) { |
201 FilePath dir = FilePath(directories.top()); | 203 FilePath dir = FilePath(directories.top()); |
202 directories.pop(); | 204 directories.pop(); |
203 success = (rmdir(dir.value().c_str()) == 0); | 205 success = (rmdir(dir.value().c_str()) == 0); |
204 } | 206 } |
(...skipping 23 matching lines...) Expand all Loading... |
228 | 230 |
229 Delete(from_path, true); | 231 Delete(from_path, true); |
230 return true; | 232 return true; |
231 } | 233 } |
232 | 234 |
233 bool ReplaceFile(const FilePath& from_path, const FilePath& to_path) { | 235 bool ReplaceFile(const FilePath& from_path, const FilePath& to_path) { |
234 base::ThreadRestrictions::AssertIOAllowed(); | 236 base::ThreadRestrictions::AssertIOAllowed(); |
235 return (rename(from_path.value().c_str(), to_path.value().c_str()) == 0); | 237 return (rename(from_path.value().c_str(), to_path.value().c_str()) == 0); |
236 } | 238 } |
237 | 239 |
238 bool CopyDirectory(const base::FilePath& from_path, | 240 bool CopyDirectory(const FilePath& from_path, |
239 const base::FilePath& to_path, | 241 const FilePath& to_path, |
240 bool recursive) { | 242 bool recursive) { |
241 base::ThreadRestrictions::AssertIOAllowed(); | 243 base::ThreadRestrictions::AssertIOAllowed(); |
242 // Some old callers of CopyDirectory want it to support wildcards. | 244 // Some old callers of CopyDirectory want it to support wildcards. |
243 // After some discussion, we decided to fix those callers. | 245 // After some discussion, we decided to fix those callers. |
244 // Break loudly here if anyone tries to do this. | 246 // Break loudly here if anyone tries to do this. |
| 247 // TODO(evanm): remove this once we're sure it's ok. |
245 DCHECK(to_path.value().find('*') == std::string::npos); | 248 DCHECK(to_path.value().find('*') == std::string::npos); |
246 DCHECK(from_path.value().find('*') == std::string::npos); | 249 DCHECK(from_path.value().find('*') == std::string::npos); |
247 | 250 |
248 char top_dir[PATH_MAX]; | 251 char top_dir[PATH_MAX]; |
249 if (base::strlcpy(top_dir, from_path.value().c_str(), | 252 if (base::strlcpy(top_dir, from_path.value().c_str(), |
250 arraysize(top_dir)) >= arraysize(top_dir)) { | 253 arraysize(top_dir)) >= arraysize(top_dir)) { |
251 return false; | 254 return false; |
252 } | 255 } |
253 | 256 |
254 // This function does not properly handle destinations within the source | 257 // This function does not properly handle destinations within the source |
(...skipping 16 matching lines...) Expand all Loading... |
271 return false; | 274 return false; |
272 | 275 |
273 bool success = true; | 276 bool success = true; |
274 int traverse_type = FileEnumerator::FILES | FileEnumerator::SHOW_SYM_LINKS; | 277 int traverse_type = FileEnumerator::FILES | FileEnumerator::SHOW_SYM_LINKS; |
275 if (recursive) | 278 if (recursive) |
276 traverse_type |= FileEnumerator::DIRECTORIES; | 279 traverse_type |= FileEnumerator::DIRECTORIES; |
277 FileEnumerator traversal(from_path, recursive, traverse_type); | 280 FileEnumerator traversal(from_path, recursive, traverse_type); |
278 | 281 |
279 // We have to mimic windows behavior here. |to_path| may not exist yet, | 282 // We have to mimic windows behavior here. |to_path| may not exist yet, |
280 // start the loop with |to_path|. | 283 // start the loop with |to_path|. |
281 struct stat from_stat; | 284 FileEnumerator::FindInfo info; |
282 FilePath current = from_path; | 285 FilePath current = from_path; |
283 if (stat(from_path.value().c_str(), &from_stat) < 0) { | 286 if (stat(from_path.value().c_str(), &info.stat) < 0) { |
284 DLOG(ERROR) << "CopyDirectory() couldn't stat source directory: " | 287 DLOG(ERROR) << "CopyDirectory() couldn't stat source directory: " |
285 << from_path.value() << " errno = " << errno; | 288 << from_path.value() << " errno = " << errno; |
286 success = false; | 289 success = false; |
287 } | 290 } |
288 struct stat to_path_stat; | 291 struct stat to_path_stat; |
289 FilePath from_path_base = from_path; | 292 FilePath from_path_base = from_path; |
290 if (recursive && stat(to_path.value().c_str(), &to_path_stat) == 0 && | 293 if (recursive && stat(to_path.value().c_str(), &to_path_stat) == 0 && |
291 S_ISDIR(to_path_stat.st_mode)) { | 294 S_ISDIR(to_path_stat.st_mode)) { |
292 // If the destination already exists and is a directory, then the | 295 // If the destination already exists and is a directory, then the |
293 // top level of source needs to be copied. | 296 // top level of source needs to be copied. |
294 from_path_base = from_path.DirName(); | 297 from_path_base = from_path.DirName(); |
295 } | 298 } |
296 | 299 |
297 // The Windows version of this function assumes that non-recursive calls | 300 // The Windows version of this function assumes that non-recursive calls |
298 // will always have a directory for from_path. | 301 // will always have a directory for from_path. |
299 DCHECK(recursive || S_ISDIR(from_stat.st_mode)); | 302 DCHECK(recursive || S_ISDIR(info.stat.st_mode)); |
300 | 303 |
301 while (success && !current.empty()) { | 304 while (success && !current.empty()) { |
302 // current is the source path, including from_path, so append | 305 // current is the source path, including from_path, so append |
303 // the suffix after from_path to to_path to create the target_path. | 306 // the suffix after from_path to to_path to create the target_path. |
304 FilePath target_path(to_path); | 307 FilePath target_path(to_path); |
305 if (from_path_base != current) { | 308 if (from_path_base != current) { |
306 if (!from_path_base.AppendRelativePath(current, &target_path)) { | 309 if (!from_path_base.AppendRelativePath(current, &target_path)) { |
307 success = false; | 310 success = false; |
308 break; | 311 break; |
309 } | 312 } |
310 } | 313 } |
311 | 314 |
312 if (S_ISDIR(from_stat.st_mode)) { | 315 if (S_ISDIR(info.stat.st_mode)) { |
313 if (mkdir(target_path.value().c_str(), from_stat.st_mode & 01777) != 0 && | 316 if (mkdir(target_path.value().c_str(), info.stat.st_mode & 01777) != 0 && |
314 errno != EEXIST) { | 317 errno != EEXIST) { |
315 DLOG(ERROR) << "CopyDirectory() couldn't create directory: " | 318 DLOG(ERROR) << "CopyDirectory() couldn't create directory: " |
316 << target_path.value() << " errno = " << errno; | 319 << target_path.value() << " errno = " << errno; |
317 success = false; | 320 success = false; |
318 } | 321 } |
319 } else if (S_ISREG(from_stat.st_mode)) { | 322 } else if (S_ISREG(info.stat.st_mode)) { |
320 if (!CopyFile(current, target_path)) { | 323 if (!CopyFile(current, target_path)) { |
321 DLOG(ERROR) << "CopyDirectory() couldn't create file: " | 324 DLOG(ERROR) << "CopyDirectory() couldn't create file: " |
322 << target_path.value(); | 325 << target_path.value(); |
323 success = false; | 326 success = false; |
324 } | 327 } |
325 } else { | 328 } else { |
326 DLOG(WARNING) << "CopyDirectory() skipping non-regular file: " | 329 DLOG(WARNING) << "CopyDirectory() skipping non-regular file: " |
327 << current.value(); | 330 << current.value(); |
328 } | 331 } |
329 | 332 |
330 current = traversal.Next(); | 333 current = traversal.Next(); |
331 if (!current.empty()) | 334 traversal.GetFindInfo(&info); |
332 from_stat = traversal.GetInfo().stat(); | |
333 } | 335 } |
334 | 336 |
335 return success; | 337 return success; |
336 } | 338 } |
337 | 339 |
338 bool PathExists(const FilePath& path) { | 340 bool PathExists(const FilePath& path) { |
339 base::ThreadRestrictions::AssertIOAllowed(); | 341 base::ThreadRestrictions::AssertIOAllowed(); |
340 return access(path.value().c_str(), F_OK) == 0; | 342 return access(path.value().c_str(), F_OK) == 0; |
341 } | 343 } |
342 | 344 |
(...skipping 332 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
675 return true; | 677 return true; |
676 } | 678 } |
677 | 679 |
678 // Sets the current working directory for the process. | 680 // Sets the current working directory for the process. |
679 bool SetCurrentDirectory(const FilePath& path) { | 681 bool SetCurrentDirectory(const FilePath& path) { |
680 base::ThreadRestrictions::AssertIOAllowed(); | 682 base::ThreadRestrictions::AssertIOAllowed(); |
681 int ret = chdir(path.value().c_str()); | 683 int ret = chdir(path.value().c_str()); |
682 return !ret; | 684 return !ret; |
683 } | 685 } |
684 | 686 |
| 687 /////////////////////////////////////////////// |
| 688 // FileEnumerator |
| 689 |
| 690 FileEnumerator::FileEnumerator(const FilePath& root_path, |
| 691 bool recursive, |
| 692 int file_type) |
| 693 : current_directory_entry_(0), |
| 694 root_path_(root_path), |
| 695 recursive_(recursive), |
| 696 file_type_(file_type) { |
| 697 // INCLUDE_DOT_DOT must not be specified if recursive. |
| 698 DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_))); |
| 699 pending_paths_.push(root_path); |
| 700 } |
| 701 |
| 702 FileEnumerator::FileEnumerator(const FilePath& root_path, |
| 703 bool recursive, |
| 704 int file_type, |
| 705 const FilePath::StringType& pattern) |
| 706 : current_directory_entry_(0), |
| 707 root_path_(root_path), |
| 708 recursive_(recursive), |
| 709 file_type_(file_type), |
| 710 pattern_(root_path.Append(pattern).value()) { |
| 711 // INCLUDE_DOT_DOT must not be specified if recursive. |
| 712 DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_))); |
| 713 // The Windows version of this code appends the pattern to the root_path, |
| 714 // potentially only matching against items in the top-most directory. |
| 715 // Do the same here. |
| 716 if (pattern.empty()) |
| 717 pattern_ = FilePath::StringType(); |
| 718 pending_paths_.push(root_path); |
| 719 } |
| 720 |
| 721 FileEnumerator::~FileEnumerator() { |
| 722 } |
| 723 |
| 724 FilePath FileEnumerator::Next() { |
| 725 ++current_directory_entry_; |
| 726 |
| 727 // While we've exhausted the entries in the current directory, do the next |
| 728 while (current_directory_entry_ >= directory_entries_.size()) { |
| 729 if (pending_paths_.empty()) |
| 730 return FilePath(); |
| 731 |
| 732 root_path_ = pending_paths_.top(); |
| 733 root_path_ = root_path_.StripTrailingSeparators(); |
| 734 pending_paths_.pop(); |
| 735 |
| 736 std::vector<DirectoryEntryInfo> entries; |
| 737 if (!ReadDirectory(&entries, root_path_, file_type_ & SHOW_SYM_LINKS)) |
| 738 continue; |
| 739 |
| 740 directory_entries_.clear(); |
| 741 current_directory_entry_ = 0; |
| 742 for (std::vector<DirectoryEntryInfo>::const_iterator |
| 743 i = entries.begin(); i != entries.end(); ++i) { |
| 744 FilePath full_path = root_path_.Append(i->filename); |
| 745 if (ShouldSkip(full_path)) |
| 746 continue; |
| 747 |
| 748 if (pattern_.size() && |
| 749 fnmatch(pattern_.c_str(), full_path.value().c_str(), FNM_NOESCAPE)) |
| 750 continue; |
| 751 |
| 752 if (recursive_ && S_ISDIR(i->stat.st_mode)) |
| 753 pending_paths_.push(full_path); |
| 754 |
| 755 if ((S_ISDIR(i->stat.st_mode) && (file_type_ & DIRECTORIES)) || |
| 756 (!S_ISDIR(i->stat.st_mode) && (file_type_ & FILES))) |
| 757 directory_entries_.push_back(*i); |
| 758 } |
| 759 } |
| 760 |
| 761 return root_path_.Append(directory_entries_[current_directory_entry_ |
| 762 ].filename); |
| 763 } |
| 764 |
| 765 void FileEnumerator::GetFindInfo(FindInfo* info) { |
| 766 DCHECK(info); |
| 767 |
| 768 if (current_directory_entry_ >= directory_entries_.size()) |
| 769 return; |
| 770 |
| 771 DirectoryEntryInfo* cur_entry = &directory_entries_[current_directory_entry_]; |
| 772 memcpy(&(info->stat), &(cur_entry->stat), sizeof(info->stat)); |
| 773 info->filename.assign(cur_entry->filename.value()); |
| 774 } |
| 775 |
| 776 // static |
| 777 bool FileEnumerator::IsDirectory(const FindInfo& info) { |
| 778 return S_ISDIR(info.stat.st_mode); |
| 779 } |
| 780 |
| 781 // static |
| 782 FilePath FileEnumerator::GetFilename(const FindInfo& find_info) { |
| 783 return FilePath(find_info.filename); |
| 784 } |
| 785 |
| 786 // static |
| 787 int64 FileEnumerator::GetFilesize(const FindInfo& find_info) { |
| 788 return find_info.stat.st_size; |
| 789 } |
| 790 |
| 791 // static |
| 792 base::Time FileEnumerator::GetLastModifiedTime(const FindInfo& find_info) { |
| 793 return base::Time::FromTimeT(find_info.stat.st_mtime); |
| 794 } |
| 795 |
| 796 bool FileEnumerator::ReadDirectory(std::vector<DirectoryEntryInfo>* entries, |
| 797 const FilePath& source, bool show_links) { |
| 798 base::ThreadRestrictions::AssertIOAllowed(); |
| 799 DIR* dir = opendir(source.value().c_str()); |
| 800 if (!dir) |
| 801 return false; |
| 802 |
| 803 #if !defined(OS_LINUX) && !defined(OS_MACOSX) && !defined(OS_BSD) && \ |
| 804 !defined(OS_SOLARIS) && !defined(OS_ANDROID) |
| 805 #error Port warning: depending on the definition of struct dirent, \ |
| 806 additional space for pathname may be needed |
| 807 #endif |
| 808 |
| 809 struct dirent dent_buf; |
| 810 struct dirent* dent; |
| 811 while (readdir_r(dir, &dent_buf, &dent) == 0 && dent) { |
| 812 DirectoryEntryInfo info; |
| 813 info.filename = FilePath(dent->d_name); |
| 814 |
| 815 FilePath full_name = source.Append(dent->d_name); |
| 816 int ret; |
| 817 if (show_links) |
| 818 ret = lstat(full_name.value().c_str(), &info.stat); |
| 819 else |
| 820 ret = stat(full_name.value().c_str(), &info.stat); |
| 821 if (ret < 0) { |
| 822 // Print the stat() error message unless it was ENOENT and we're |
| 823 // following symlinks. |
| 824 if (!(errno == ENOENT && !show_links)) { |
| 825 DPLOG(ERROR) << "Couldn't stat " |
| 826 << source.Append(dent->d_name).value(); |
| 827 } |
| 828 memset(&info.stat, 0, sizeof(info.stat)); |
| 829 } |
| 830 entries->push_back(info); |
| 831 } |
| 832 |
| 833 closedir(dir); |
| 834 return true; |
| 835 } |
| 836 |
685 bool NormalizeFilePath(const FilePath& path, FilePath* normalized_path) { | 837 bool NormalizeFilePath(const FilePath& path, FilePath* normalized_path) { |
686 FilePath real_path_result; | 838 FilePath real_path_result; |
687 if (!RealPath(path, &real_path_result)) | 839 if (!RealPath(path, &real_path_result)) |
688 return false; | 840 return false; |
689 | 841 |
690 // To be consistant with windows, fail if |real_path_result| is a | 842 // To be consistant with windows, fail if |real_path_result| is a |
691 // directory. | 843 // directory. |
692 stat_wrapper_t file_info; | 844 stat_wrapper_t file_info; |
693 if (CallStat(real_path_result.value().c_str(), &file_info) != 0 || | 845 if (CallStat(real_path_result.value().c_str(), &file_info) != 0 || |
694 S_ISDIR(file_info.st_mode)) | 846 S_ISDIR(file_info.st_mode)) |
(...skipping 214 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
909 kFileSystemRoot, path, kRootUid, allowed_group_ids); | 1061 kFileSystemRoot, path, kRootUid, allowed_group_ids); |
910 } | 1062 } |
911 #endif // defined(OS_MACOSX) && !defined(OS_IOS) | 1063 #endif // defined(OS_MACOSX) && !defined(OS_IOS) |
912 | 1064 |
913 int GetMaximumPathComponentLength(const FilePath& path) { | 1065 int GetMaximumPathComponentLength(const FilePath& path) { |
914 base::ThreadRestrictions::AssertIOAllowed(); | 1066 base::ThreadRestrictions::AssertIOAllowed(); |
915 return pathconf(path.value().c_str(), _PC_NAME_MAX); | 1067 return pathconf(path.value().c_str(), _PC_NAME_MAX); |
916 } | 1068 } |
917 | 1069 |
918 } // namespace file_util | 1070 } // namespace file_util |
OLD | NEW |