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 12 namespace base { 13 14 // FileEnumerator::FileInfo ---------------------------------------------------- 15 16 FileEnumerator::FileInfo::FileInfo() { 17 memset(&find_data_, 0, sizeof(find_data_)); 18 } 19 20 bool FileEnumerator::FileInfo::IsDirectory() const { 21 return (find_data_.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; 22 } 23 24 FilePath FileEnumerator::FileInfo::GetName() const { 25 return FilePath(find_data_.cFileName); 26 } 27 28 int64 FileEnumerator::FileInfo::GetSize() const { 29 ULARGE_INTEGER size; 30 size.HighPart = find_data_.nFileSizeHigh; 31 size.LowPart = find_data_.nFileSizeLow; 32 DCHECK_LE(size.QuadPart, std::numeric_limits<int64>::max()); 33 return static_cast<int64>(size.QuadPart); 34 } 35 36 base::Time FileEnumerator::FileInfo::GetLastModifiedTime() const { 37 return base::Time::FromFileTime(find_data_.ftLastWriteTime); 38 } 39 40 // FileEnumerator -------------------------------------------------------------- 41 42 FileEnumerator::FileEnumerator(const FilePath& root_path, 43 bool recursive, 44 int file_type) 45 : recursive_(recursive), 46 file_type_(file_type), 47 has_find_data_(false), 48 find_handle_(INVALID_HANDLE_VALUE) { 49 // INCLUDE_DOT_DOT must not be specified if recursive. 50 DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_))); 51 memset(&find_data_, 0, sizeof(find_data_)); 52 pending_paths_.push(root_path); 53 } 54 55 FileEnumerator::FileEnumerator(const FilePath& root_path, 56 bool recursive, 57 int file_type, 58 const FilePath::StringType& pattern) 59 : recursive_(recursive), 60 file_type_(file_type), 61 has_find_data_(false), 62 pattern_(pattern), 63 find_handle_(INVALID_HANDLE_VALUE) { 64 // INCLUDE_DOT_DOT must not be specified if recursive. 65 DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_))); 66 memset(&find_data_, 0, sizeof(find_data_)); 67 pending_paths_.push(root_path); 68 } 69 70 FileEnumerator::~FileEnumerator() { 71 if (find_handle_ != INVALID_HANDLE_VALUE) 72 FindClose(find_handle_); 73 } 74 75 FileEnumerator::FileInfo FileEnumerator::GetInfo() const { 76 if (!has_find_data_) { 77 NOTREACHED(); 78 return FileInfo(); 79 } 80 FileInfo ret; 81 memcpy(&ret.find_data_, &find_data_, sizeof(find_data_)); 82 return ret; 83 } 84 85 FilePath FileEnumerator::Next() { 86 base::ThreadRestrictions::AssertIOAllowed(); 87 88 while (has_find_data_ || !pending_paths_.empty()) { 89 if (!has_find_data_) { 90 // The last find FindFirstFile operation is done, prepare a new one. 91 root_path_ = pending_paths_.top(); 92 pending_paths_.pop(); 93 94 // Start a new find operation. 95 FilePath src = root_path_; 96 97 if (pattern_.empty()) 98 src = src.Append(L"*"); // No pattern = match everything. 99 else 100 src = src.Append(pattern_); 101 102 find_handle_ = FindFirstFile(src.value().c_str(), &find_data_); 103 has_find_data_ = true; 104 } else { 105 // Search for the next file/directory. 106 if (!FindNextFile(find_handle_, &find_data_)) { 107 FindClose(find_handle_); 108 find_handle_ = INVALID_HANDLE_VALUE; 109 } 110 } 111 112 if (INVALID_HANDLE_VALUE == find_handle_) { 113 has_find_data_ = false; 114 115 // This is reached when we have finished a directory and are advancing to 116 // the next one in the queue. We applied the pattern (if any) to the files 117 // in the root search directory, but for those directories which were 118 // matched, we want to enumerate all files inside them. This will happen 119 // when the handle is empty. 120 pattern_ = FilePath::StringType(); 121 122 continue; 123 } 124 125 FilePath cur_file(find_data_.cFileName); 126 if (ShouldSkip(cur_file)) 127 continue; 128 129 // Construct the absolute filename. 130 cur_file = root_path_.Append(find_data_.cFileName); 131 132 if (find_data_.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { 133 if (recursive_) { 134 // If |cur_file| is a directory, and we are doing recursive searching, 135 // add it to pending_paths_ so we scan it after we finish scanning this 136 // directory. 137 pending_paths_.push(cur_file); 138 } 139 if (file_type_ & FileEnumerator::DIRECTORIES) 140 return cur_file; 141 } else if (file_type_ & FileEnumerator::FILES) { 142 return cur_file; 143 } 144 } 145 146 return FilePath(); 147 } 148 149 } // namespace base 150