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> | |
11 #include <libgen.h> | 10 #include <libgen.h> |
12 #include <limits.h> | 11 #include <limits.h> |
13 #include <stdio.h> | 12 #include <stdio.h> |
14 #include <stdlib.h> | 13 #include <stdlib.h> |
15 #include <string.h> | 14 #include <string.h> |
16 #include <sys/errno.h> | 15 #include <sys/errno.h> |
17 #include <sys/mman.h> | 16 #include <sys/mman.h> |
18 #include <sys/param.h> | 17 #include <sys/param.h> |
19 #include <sys/stat.h> | 18 #include <sys/stat.h> |
20 #include <sys/time.h> | 19 #include <sys/time.h> |
21 #include <sys/types.h> | 20 #include <sys/types.h> |
22 #include <time.h> | 21 #include <time.h> |
23 #include <unistd.h> | 22 #include <unistd.h> |
24 | 23 |
25 #if defined(OS_MACOSX) | 24 #if defined(OS_MACOSX) |
26 #include <AvailabilityMacros.h> | 25 #include <AvailabilityMacros.h> |
27 #include "base/mac/foundation_util.h" | 26 #include "base/mac/foundation_util.h" |
28 #elif !defined(OS_ANDROID) | 27 #elif !defined(OS_ANDROID) |
29 #include <glib.h> | 28 #include <glib.h> |
30 #endif | 29 #endif |
31 | 30 |
32 #include <fstream> | 31 #include <fstream> |
33 | 32 |
34 #include "base/basictypes.h" | 33 #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; |
61 using base::FilePath; | 62 using base::FilePath; |
62 using base::MakeAbsoluteFilePath; | 63 using base::MakeAbsoluteFilePath; |
63 | 64 |
64 namespace base { | 65 namespace base { |
65 | 66 |
66 FilePath MakeAbsoluteFilePath(const FilePath& input) { | 67 FilePath MakeAbsoluteFilePath(const FilePath& input) { |
67 base::ThreadRestrictions::AssertIOAllowed(); | 68 base::ThreadRestrictions::AssertIOAllowed(); |
68 char full_path[PATH_MAX]; | 69 char full_path[PATH_MAX]; |
69 if (realpath(input.value().c_str(), full_path) == NULL) | 70 if (realpath(input.value().c_str(), full_path) == NULL) |
70 return FilePath(); | 71 return FilePath(); |
(...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
183 return (rmdir(path_str) == 0); | 184 return (rmdir(path_str) == 0); |
184 | 185 |
185 bool success = true; | 186 bool success = true; |
186 std::stack<std::string> directories; | 187 std::stack<std::string> directories; |
187 directories.push(path.value()); | 188 directories.push(path.value()); |
188 FileEnumerator traversal(path, true, | 189 FileEnumerator traversal(path, true, |
189 FileEnumerator::FILES | FileEnumerator::DIRECTORIES | | 190 FileEnumerator::FILES | FileEnumerator::DIRECTORIES | |
190 FileEnumerator::SHOW_SYM_LINKS); | 191 FileEnumerator::SHOW_SYM_LINKS); |
191 for (FilePath current = traversal.Next(); success && !current.empty(); | 192 for (FilePath current = traversal.Next(); success && !current.empty(); |
192 current = traversal.Next()) { | 193 current = traversal.Next()) { |
193 FileEnumerator::FindInfo info; | 194 if (traversal.GetInfo().IsDirectory()) |
194 traversal.GetFindInfo(&info); | |
195 | |
196 if (S_ISDIR(info.stat.st_mode)) | |
197 directories.push(current.value()); | 195 directories.push(current.value()); |
198 else | 196 else |
199 success = (unlink(current.value().c_str()) == 0); | 197 success = (unlink(current.value().c_str()) == 0); |
200 } | 198 } |
201 | 199 |
202 while (success && !directories.empty()) { | 200 while (success && !directories.empty()) { |
203 FilePath dir = FilePath(directories.top()); | 201 FilePath dir = FilePath(directories.top()); |
204 directories.pop(); | 202 directories.pop(); |
205 success = (rmdir(dir.value().c_str()) == 0); | 203 success = (rmdir(dir.value().c_str()) == 0); |
206 } | 204 } |
(...skipping 23 matching lines...) Expand all Loading... |
230 | 228 |
231 Delete(from_path, true); | 229 Delete(from_path, true); |
232 return true; | 230 return true; |
233 } | 231 } |
234 | 232 |
235 bool ReplaceFile(const FilePath& from_path, const FilePath& to_path) { | 233 bool ReplaceFile(const FilePath& from_path, const FilePath& to_path) { |
236 base::ThreadRestrictions::AssertIOAllowed(); | 234 base::ThreadRestrictions::AssertIOAllowed(); |
237 return (rename(from_path.value().c_str(), to_path.value().c_str()) == 0); | 235 return (rename(from_path.value().c_str(), to_path.value().c_str()) == 0); |
238 } | 236 } |
239 | 237 |
240 bool CopyDirectory(const FilePath& from_path, | 238 bool CopyDirectory(const base::FilePath& from_path, |
241 const FilePath& to_path, | 239 const base::FilePath& to_path, |
242 bool recursive) { | 240 bool recursive) { |
243 base::ThreadRestrictions::AssertIOAllowed(); | 241 base::ThreadRestrictions::AssertIOAllowed(); |
244 // Some old callers of CopyDirectory want it to support wildcards. | 242 // Some old callers of CopyDirectory want it to support wildcards. |
245 // After some discussion, we decided to fix those callers. | 243 // After some discussion, we decided to fix those callers. |
246 // Break loudly here if anyone tries to do this. | 244 // Break loudly here if anyone tries to do this. |
247 // TODO(evanm): remove this once we're sure it's ok. | |
248 DCHECK(to_path.value().find('*') == std::string::npos); | 245 DCHECK(to_path.value().find('*') == std::string::npos); |
249 DCHECK(from_path.value().find('*') == std::string::npos); | 246 DCHECK(from_path.value().find('*') == std::string::npos); |
250 | 247 |
251 char top_dir[PATH_MAX]; | 248 char top_dir[PATH_MAX]; |
252 if (base::strlcpy(top_dir, from_path.value().c_str(), | 249 if (base::strlcpy(top_dir, from_path.value().c_str(), |
253 arraysize(top_dir)) >= arraysize(top_dir)) { | 250 arraysize(top_dir)) >= arraysize(top_dir)) { |
254 return false; | 251 return false; |
255 } | 252 } |
256 | 253 |
257 // This function does not properly handle destinations within the source | 254 // This function does not properly handle destinations within the source |
(...skipping 16 matching lines...) Expand all Loading... |
274 return false; | 271 return false; |
275 | 272 |
276 bool success = true; | 273 bool success = true; |
277 int traverse_type = FileEnumerator::FILES | FileEnumerator::SHOW_SYM_LINKS; | 274 int traverse_type = FileEnumerator::FILES | FileEnumerator::SHOW_SYM_LINKS; |
278 if (recursive) | 275 if (recursive) |
279 traverse_type |= FileEnumerator::DIRECTORIES; | 276 traverse_type |= FileEnumerator::DIRECTORIES; |
280 FileEnumerator traversal(from_path, recursive, traverse_type); | 277 FileEnumerator traversal(from_path, recursive, traverse_type); |
281 | 278 |
282 // We have to mimic windows behavior here. |to_path| may not exist yet, | 279 // We have to mimic windows behavior here. |to_path| may not exist yet, |
283 // start the loop with |to_path|. | 280 // start the loop with |to_path|. |
284 FileEnumerator::FindInfo info; | 281 struct stat from_stat; |
285 FilePath current = from_path; | 282 FilePath current = from_path; |
286 if (stat(from_path.value().c_str(), &info.stat) < 0) { | 283 if (stat(from_path.value().c_str(), &from_stat) < 0) { |
287 DLOG(ERROR) << "CopyDirectory() couldn't stat source directory: " | 284 DLOG(ERROR) << "CopyDirectory() couldn't stat source directory: " |
288 << from_path.value() << " errno = " << errno; | 285 << from_path.value() << " errno = " << errno; |
289 success = false; | 286 success = false; |
290 } | 287 } |
291 struct stat to_path_stat; | 288 struct stat to_path_stat; |
292 FilePath from_path_base = from_path; | 289 FilePath from_path_base = from_path; |
293 if (recursive && stat(to_path.value().c_str(), &to_path_stat) == 0 && | 290 if (recursive && stat(to_path.value().c_str(), &to_path_stat) == 0 && |
294 S_ISDIR(to_path_stat.st_mode)) { | 291 S_ISDIR(to_path_stat.st_mode)) { |
295 // If the destination already exists and is a directory, then the | 292 // If the destination already exists and is a directory, then the |
296 // top level of source needs to be copied. | 293 // top level of source needs to be copied. |
297 from_path_base = from_path.DirName(); | 294 from_path_base = from_path.DirName(); |
298 } | 295 } |
299 | 296 |
300 // The Windows version of this function assumes that non-recursive calls | 297 // The Windows version of this function assumes that non-recursive calls |
301 // will always have a directory for from_path. | 298 // will always have a directory for from_path. |
302 DCHECK(recursive || S_ISDIR(info.stat.st_mode)); | 299 DCHECK(recursive || S_ISDIR(from_stat.st_mode)); |
303 | 300 |
304 while (success && !current.empty()) { | 301 while (success && !current.empty()) { |
305 // current is the source path, including from_path, so append | 302 // current is the source path, including from_path, so append |
306 // the suffix after from_path to to_path to create the target_path. | 303 // the suffix after from_path to to_path to create the target_path. |
307 FilePath target_path(to_path); | 304 FilePath target_path(to_path); |
308 if (from_path_base != current) { | 305 if (from_path_base != current) { |
309 if (!from_path_base.AppendRelativePath(current, &target_path)) { | 306 if (!from_path_base.AppendRelativePath(current, &target_path)) { |
310 success = false; | 307 success = false; |
311 break; | 308 break; |
312 } | 309 } |
313 } | 310 } |
314 | 311 |
315 if (S_ISDIR(info.stat.st_mode)) { | 312 if (S_ISDIR(from_stat.st_mode)) { |
316 if (mkdir(target_path.value().c_str(), info.stat.st_mode & 01777) != 0 && | 313 if (mkdir(target_path.value().c_str(), from_stat.st_mode & 01777) != 0 && |
317 errno != EEXIST) { | 314 errno != EEXIST) { |
318 DLOG(ERROR) << "CopyDirectory() couldn't create directory: " | 315 DLOG(ERROR) << "CopyDirectory() couldn't create directory: " |
319 << target_path.value() << " errno = " << errno; | 316 << target_path.value() << " errno = " << errno; |
320 success = false; | 317 success = false; |
321 } | 318 } |
322 } else if (S_ISREG(info.stat.st_mode)) { | 319 } else if (S_ISREG(from_stat.st_mode)) { |
323 if (!CopyFile(current, target_path)) { | 320 if (!CopyFile(current, target_path)) { |
324 DLOG(ERROR) << "CopyDirectory() couldn't create file: " | 321 DLOG(ERROR) << "CopyDirectory() couldn't create file: " |
325 << target_path.value(); | 322 << target_path.value(); |
326 success = false; | 323 success = false; |
327 } | 324 } |
328 } else { | 325 } else { |
329 DLOG(WARNING) << "CopyDirectory() skipping non-regular file: " | 326 DLOG(WARNING) << "CopyDirectory() skipping non-regular file: " |
330 << current.value(); | 327 << current.value(); |
331 } | 328 } |
332 | 329 |
333 current = traversal.Next(); | 330 current = traversal.Next(); |
334 traversal.GetFindInfo(&info); | 331 if (!current.empty()) |
| 332 from_stat = traversal.GetInfo().stat(); |
335 } | 333 } |
336 | 334 |
337 return success; | 335 return success; |
338 } | 336 } |
339 | 337 |
340 bool PathExists(const FilePath& path) { | 338 bool PathExists(const FilePath& path) { |
341 base::ThreadRestrictions::AssertIOAllowed(); | 339 base::ThreadRestrictions::AssertIOAllowed(); |
342 return access(path.value().c_str(), F_OK) == 0; | 340 return access(path.value().c_str(), F_OK) == 0; |
343 } | 341 } |
344 | 342 |
(...skipping 332 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
677 return true; | 675 return true; |
678 } | 676 } |
679 | 677 |
680 // Sets the current working directory for the process. | 678 // Sets the current working directory for the process. |
681 bool SetCurrentDirectory(const FilePath& path) { | 679 bool SetCurrentDirectory(const FilePath& path) { |
682 base::ThreadRestrictions::AssertIOAllowed(); | 680 base::ThreadRestrictions::AssertIOAllowed(); |
683 int ret = chdir(path.value().c_str()); | 681 int ret = chdir(path.value().c_str()); |
684 return !ret; | 682 return !ret; |
685 } | 683 } |
686 | 684 |
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 | |
837 bool NormalizeFilePath(const FilePath& path, FilePath* normalized_path) { | 685 bool NormalizeFilePath(const FilePath& path, FilePath* normalized_path) { |
838 FilePath real_path_result; | 686 FilePath real_path_result; |
839 if (!RealPath(path, &real_path_result)) | 687 if (!RealPath(path, &real_path_result)) |
840 return false; | 688 return false; |
841 | 689 |
842 // To be consistant with windows, fail if |real_path_result| is a | 690 // To be consistant with windows, fail if |real_path_result| is a |
843 // directory. | 691 // directory. |
844 stat_wrapper_t file_info; | 692 stat_wrapper_t file_info; |
845 if (CallStat(real_path_result.value().c_str(), &file_info) != 0 || | 693 if (CallStat(real_path_result.value().c_str(), &file_info) != 0 || |
846 S_ISDIR(file_info.st_mode)) | 694 S_ISDIR(file_info.st_mode)) |
(...skipping 214 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1061 kFileSystemRoot, path, kRootUid, allowed_group_ids); | 909 kFileSystemRoot, path, kRootUid, allowed_group_ids); |
1062 } | 910 } |
1063 #endif // defined(OS_MACOSX) && !defined(OS_IOS) | 911 #endif // defined(OS_MACOSX) && !defined(OS_IOS) |
1064 | 912 |
1065 int GetMaximumPathComponentLength(const FilePath& path) { | 913 int GetMaximumPathComponentLength(const FilePath& path) { |
1066 base::ThreadRestrictions::AssertIOAllowed(); | 914 base::ThreadRestrictions::AssertIOAllowed(); |
1067 return pathconf(path.value().c_str(), _PC_NAME_MAX); | 915 return pathconf(path.value().c_str(), _PC_NAME_MAX); |
1068 } | 916 } |
1069 | 917 |
1070 } // namespace file_util | 918 } // namespace file_util |
OLD | NEW |