1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
|
// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/files/file_enumerator.h"
#include <stdint.h>
#include <string.h>
#include "base/check.h"
#include "base/check_op.h"
#include "base/compiler_specific.h"
#include "base/notreached.h"
#include "base/strings/string_util.h"
#include "base/threading/scoped_blocking_call.h"
#include "base/win/shlwapi.h"
namespace base {
namespace {
FilePath BuildSearchFilter(FileEnumerator::FolderSearchPolicy policy,
const FilePath& root_path,
const FilePath::StringType& pattern) {
// MATCH_ONLY policy filters incoming files by pattern on OS side. ALL policy
// collects all files and filters them manually.
switch (policy) {
case FileEnumerator::FolderSearchPolicy::MATCH_ONLY:
return root_path.Append(pattern);
case FileEnumerator::FolderSearchPolicy::ALL:
return root_path.Append(FILE_PATH_LITERAL("*"));
}
NOTREACHED();
}
} // namespace
// FileEnumerator::FileInfo ----------------------------------------------------
FileEnumerator::FileInfo::FileInfo() {
UNSAFE_TODO(memset(&find_data_, 0, sizeof(find_data_)));
}
bool FileEnumerator::FileInfo::IsDirectory() const {
return (find_data().dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
}
FilePath FileEnumerator::FileInfo::GetName() const {
return FilePath(find_data().cFileName);
}
int64_t FileEnumerator::FileInfo::GetSize() const {
ULARGE_INTEGER size;
size.HighPart = find_data().nFileSizeHigh;
size.LowPart = find_data().nFileSizeLow;
DCHECK_LE(size.QuadPart,
static_cast<ULONGLONG>(std::numeric_limits<int64_t>::max()));
return static_cast<int64_t>(size.QuadPart);
}
Time FileEnumerator::FileInfo::GetLastModifiedTime() const {
return Time::FromFileTime(find_data().ftLastWriteTime);
}
// FileEnumerator --------------------------------------------------------------
FileEnumerator::FileEnumerator(const FilePath& root_path,
bool recursive,
int file_type)
: FileEnumerator(root_path,
recursive,
file_type,
FilePath::StringType(),
FolderSearchPolicy::MATCH_ONLY) {}
FileEnumerator::FileEnumerator(const FilePath& root_path,
bool recursive,
int file_type,
const FilePath::StringType& pattern)
: FileEnumerator(root_path,
recursive,
file_type,
pattern,
FolderSearchPolicy::MATCH_ONLY) {}
FileEnumerator::FileEnumerator(const FilePath& root_path,
bool recursive,
int file_type,
const FilePath::StringType& pattern,
FolderSearchPolicy folder_search_policy)
: FileEnumerator(root_path,
recursive,
file_type,
pattern,
folder_search_policy,
ErrorPolicy::IGNORE_ERRORS) {}
FileEnumerator::FileEnumerator(const FilePath& root_path,
bool recursive,
int file_type,
const FilePath::StringType& pattern,
FolderSearchPolicy folder_search_policy,
ErrorPolicy error_policy)
: recursive_(recursive),
file_type_(file_type),
pattern_(!pattern.empty() ? pattern : FILE_PATH_LITERAL("*")),
folder_search_policy_(folder_search_policy),
error_policy_(error_policy) {
// INCLUDE_DOT_DOT must not be specified if recursive.
DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_)));
if (file_type_ & FileType::NAMES_ONLY) {
DCHECK(!recursive_);
DCHECK_EQ(file_type_ & ~(FileType::NAMES_ONLY | FileType::INCLUDE_DOT_DOT),
0);
file_type_ |= (FileType::FILES | FileType::DIRECTORIES);
}
UNSAFE_TODO(memset(&find_data_, 0, sizeof(find_data_)));
pending_paths_.push(root_path);
}
FileEnumerator::~FileEnumerator() {
if (find_handle_ != INVALID_HANDLE_VALUE) {
FindClose(find_handle_);
}
}
FileEnumerator::FileInfo FileEnumerator::GetInfo() const {
DCHECK(!(file_type_ & FileType::NAMES_ONLY));
CHECK(has_find_data_);
FileInfo ret;
UNSAFE_TODO(memcpy(&ret.find_data_, &find_data_, sizeof(find_data_)));
return ret;
}
FilePath FileEnumerator::Next() {
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
while (has_find_data_ || !pending_paths_.empty()) {
if (!has_find_data_) {
// The last find FindFirstFile operation is done, prepare a new one.
root_path_ = pending_paths_.top();
pending_paths_.pop();
// Start a new find operation.
const FilePath src =
BuildSearchFilter(folder_search_policy_, root_path_, pattern_);
find_handle_ = FindFirstFileEx(src.value().c_str(),
FindExInfoBasic, // Omit short name.
ChromeToWindowsType(&find_data_),
FindExSearchNameMatch, nullptr,
FIND_FIRST_EX_LARGE_FETCH);
has_find_data_ = true;
} else {
// Search for the next file/directory.
if (!FindNextFile(find_handle_, ChromeToWindowsType(&find_data_))) {
FindClose(find_handle_);
find_handle_ = INVALID_HANDLE_VALUE;
}
}
DWORD last_error = GetLastError();
if (INVALID_HANDLE_VALUE == find_handle_) {
has_find_data_ = false;
// MATCH_ONLY policy clears pattern for matched subfolders. ALL policy
// applies pattern for all subfolders.
if (folder_search_policy_ == FolderSearchPolicy::MATCH_ONLY) {
// This is reached when we have finished a directory and are advancing
// to the next one in the queue. We applied the pattern (if any) to the
// files in the root search directory, but for those directories which
// were matched, we want to enumerate all files inside them. This will
// happen when the handle is empty.
pattern_ = FILE_PATH_LITERAL("*");
}
if (last_error == ERROR_NO_MORE_FILES ||
error_policy_ == ErrorPolicy::IGNORE_ERRORS) {
continue;
}
error_ = File::OSErrorToFileError(last_error);
return FilePath();
}
const FilePath filename(find_data().cFileName);
if (ShouldSkip(filename)) {
continue;
}
const bool is_dir =
(find_data().dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
const FilePath abs_path = root_path_.Append(filename);
// Check if directory should be processed recursive.
if (is_dir && recursive_) {
// If |cur_file| is a directory, and we are doing recursive searching,
// add it to pending_paths_ so we scan it after we finish scanning this
// directory. However, don't do recursion through reparse points or we
// may end up with an infinite cycle.
DWORD attributes = GetFileAttributes(abs_path.value().c_str());
if (!(attributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
pending_paths_.push(abs_path);
}
}
if (IsTypeMatched(is_dir) && IsPatternMatched(filename)) {
return abs_path;
}
}
return FilePath();
}
bool FileEnumerator::IsPatternMatched(const FilePath& src) const {
switch (folder_search_policy_) {
case FolderSearchPolicy::MATCH_ONLY:
// MATCH_ONLY policy filters by pattern on search request, so all found
// files already fits to pattern.
return true;
case FolderSearchPolicy::ALL:
// ALL policy enumerates all files, we need to check pattern match
// manually.
return PathMatchSpec(src.value().c_str(), pattern_.c_str()) == TRUE;
}
NOTREACHED();
}
} // namespace base
|