OLD | NEW |
| (Empty) |
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "base/files/file_enumerator.h" | |
6 | |
7 #include <string.h> | |
8 | |
9 #include "base/logging.h" | |
10 #include "base/threading/thread_restrictions.h" | |
11 #include "base/win/windows_version.h" | |
12 | |
13 namespace base { | |
14 | |
15 // FileEnumerator::FileInfo ---------------------------------------------------- | |
16 | |
17 FileEnumerator::FileInfo::FileInfo() { | |
18 memset(&find_data_, 0, sizeof(find_data_)); | |
19 } | |
20 | |
21 bool FileEnumerator::FileInfo::IsDirectory() const { | |
22 return (find_data_.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; | |
23 } | |
24 | |
25 FilePath FileEnumerator::FileInfo::GetName() const { | |
26 return FilePath(find_data_.cFileName); | |
27 } | |
28 | |
29 int64 FileEnumerator::FileInfo::GetSize() const { | |
30 ULARGE_INTEGER size; | |
31 size.HighPart = find_data_.nFileSizeHigh; | |
32 size.LowPart = find_data_.nFileSizeLow; | |
33 DCHECK_LE(size.QuadPart, std::numeric_limits<int64>::max()); | |
34 return static_cast<int64>(size.QuadPart); | |
35 } | |
36 | |
37 base::Time FileEnumerator::FileInfo::GetLastModifiedTime() const { | |
38 return base::Time::FromFileTime(find_data_.ftLastWriteTime); | |
39 } | |
40 | |
41 // FileEnumerator -------------------------------------------------------------- | |
42 | |
43 FileEnumerator::FileEnumerator(const FilePath& root_path, | |
44 bool recursive, | |
45 int file_type) | |
46 : recursive_(recursive), | |
47 file_type_(file_type), | |
48 has_find_data_(false), | |
49 find_handle_(INVALID_HANDLE_VALUE) { | |
50 // INCLUDE_DOT_DOT must not be specified if recursive. | |
51 DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_))); | |
52 memset(&find_data_, 0, sizeof(find_data_)); | |
53 pending_paths_.push(root_path); | |
54 } | |
55 | |
56 FileEnumerator::FileEnumerator(const FilePath& root_path, | |
57 bool recursive, | |
58 int file_type, | |
59 const FilePath::StringType& pattern) | |
60 : recursive_(recursive), | |
61 file_type_(file_type), | |
62 has_find_data_(false), | |
63 pattern_(pattern), | |
64 find_handle_(INVALID_HANDLE_VALUE) { | |
65 // INCLUDE_DOT_DOT must not be specified if recursive. | |
66 DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_))); | |
67 memset(&find_data_, 0, sizeof(find_data_)); | |
68 pending_paths_.push(root_path); | |
69 } | |
70 | |
71 FileEnumerator::~FileEnumerator() { | |
72 if (find_handle_ != INVALID_HANDLE_VALUE) | |
73 FindClose(find_handle_); | |
74 } | |
75 | |
76 FileEnumerator::FileInfo FileEnumerator::GetInfo() const { | |
77 if (!has_find_data_) { | |
78 NOTREACHED(); | |
79 return FileInfo(); | |
80 } | |
81 FileInfo ret; | |
82 memcpy(&ret.find_data_, &find_data_, sizeof(find_data_)); | |
83 return ret; | |
84 } | |
85 | |
86 FilePath FileEnumerator::Next() { | |
87 base::ThreadRestrictions::AssertIOAllowed(); | |
88 | |
89 while (has_find_data_ || !pending_paths_.empty()) { | |
90 if (!has_find_data_) { | |
91 // The last find FindFirstFile operation is done, prepare a new one. | |
92 root_path_ = pending_paths_.top(); | |
93 pending_paths_.pop(); | |
94 | |
95 // Start a new find operation. | |
96 FilePath src = root_path_; | |
97 | |
98 if (pattern_.empty()) | |
99 src = src.Append(L"*"); // No pattern = match everything. | |
100 else | |
101 src = src.Append(pattern_); | |
102 | |
103 if (base::win::GetVersion() >= base::win::VERSION_WIN7) { | |
104 // Use a "large fetch" on newer Windows which should speed up large | |
105 // enumerations (we seldom abort in the middle). | |
106 find_handle_ = FindFirstFileEx(src.value().c_str(), | |
107 FindExInfoBasic, // Omit short name. | |
108 &find_data_, | |
109 FindExSearchNameMatch, | |
110 NULL, | |
111 FIND_FIRST_EX_LARGE_FETCH); | |
112 } else { | |
113 find_handle_ = FindFirstFile(src.value().c_str(), &find_data_); | |
114 } | |
115 has_find_data_ = true; | |
116 } else { | |
117 // Search for the next file/directory. | |
118 if (!FindNextFile(find_handle_, &find_data_)) { | |
119 FindClose(find_handle_); | |
120 find_handle_ = INVALID_HANDLE_VALUE; | |
121 } | |
122 } | |
123 | |
124 if (INVALID_HANDLE_VALUE == find_handle_) { | |
125 has_find_data_ = false; | |
126 | |
127 // This is reached when we have finished a directory and are advancing to | |
128 // the next one in the queue. We applied the pattern (if any) to the files | |
129 // in the root search directory, but for those directories which were | |
130 // matched, we want to enumerate all files inside them. This will happen | |
131 // when the handle is empty. | |
132 pattern_ = FilePath::StringType(); | |
133 | |
134 continue; | |
135 } | |
136 | |
137 FilePath cur_file(find_data_.cFileName); | |
138 if (ShouldSkip(cur_file)) | |
139 continue; | |
140 | |
141 // Construct the absolute filename. | |
142 cur_file = root_path_.Append(find_data_.cFileName); | |
143 | |
144 if (find_data_.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { | |
145 if (recursive_) { | |
146 // If |cur_file| is a directory, and we are doing recursive searching, | |
147 // add it to pending_paths_ so we scan it after we finish scanning this | |
148 // directory. However, don't do recursion through reparse points or we | |
149 // may end up with an infinite cycle. | |
150 DWORD attributes = GetFileAttributes(cur_file.value().c_str()); | |
151 if (!(attributes & FILE_ATTRIBUTE_REPARSE_POINT)) | |
152 pending_paths_.push(cur_file); | |
153 } | |
154 if (file_type_ & FileEnumerator::DIRECTORIES) | |
155 return cur_file; | |
156 } else if (file_type_ & FileEnumerator::FILES) { | |
157 return cur_file; | |
158 } | |
159 } | |
160 | |
161 return FilePath(); | |
162 } | |
163 | |
164 } // namespace base | |
OLD | NEW |