1868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)// Copyright (c) 2013 The Chromium Authors. All rights reserved.
2868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
3868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)// found in the LICENSE file.
4868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
5868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/files/file_enumerator.h"
6868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
7868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include <string.h>
8868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
9868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/logging.h"
10868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/threading/thread_restrictions.h"
111e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)#include "base/win/windows_version.h"
12868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
13868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)namespace base {
14868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
15868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)// FileEnumerator::FileInfo ----------------------------------------------------
16868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
17868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)FileEnumerator::FileInfo::FileInfo() {
18868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  memset(&find_data_, 0, sizeof(find_data_));
19868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}
20868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
21868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)bool FileEnumerator::FileInfo::IsDirectory() const {
22868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  return (find_data_.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
23868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}
24868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
25868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)FilePath FileEnumerator::FileInfo::GetName() const {
26868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  return FilePath(find_data_.cFileName);
27868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}
28868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
29868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)int64 FileEnumerator::FileInfo::GetSize() const {
30868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  ULARGE_INTEGER size;
31868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  size.HighPart = find_data_.nFileSizeHigh;
32868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  size.LowPart = find_data_.nFileSizeLow;
33868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  DCHECK_LE(size.QuadPart, std::numeric_limits<int64>::max());
34868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  return static_cast<int64>(size.QuadPart);
35868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}
36868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
37868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)base::Time FileEnumerator::FileInfo::GetLastModifiedTime() const {
38868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  return base::Time::FromFileTime(find_data_.ftLastWriteTime);
39868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}
40868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
41868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)// FileEnumerator --------------------------------------------------------------
42868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
43868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)FileEnumerator::FileEnumerator(const FilePath& root_path,
44868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)                               bool recursive,
45868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)                               int file_type)
46868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    : recursive_(recursive),
47868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      file_type_(file_type),
48868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      has_find_data_(false),
49868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      find_handle_(INVALID_HANDLE_VALUE) {
50868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  // INCLUDE_DOT_DOT must not be specified if recursive.
51868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_)));
52868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  memset(&find_data_, 0, sizeof(find_data_));
53868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  pending_paths_.push(root_path);
54868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}
55868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
56868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)FileEnumerator::FileEnumerator(const FilePath& root_path,
57868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)                               bool recursive,
58868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)                               int file_type,
59868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)                               const FilePath::StringType& pattern)
60868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    : recursive_(recursive),
61868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      file_type_(file_type),
62868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      has_find_data_(false),
63868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      pattern_(pattern),
64868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      find_handle_(INVALID_HANDLE_VALUE) {
65868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  // INCLUDE_DOT_DOT must not be specified if recursive.
66868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_)));
67868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  memset(&find_data_, 0, sizeof(find_data_));
68868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  pending_paths_.push(root_path);
69868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}
70868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
71868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)FileEnumerator::~FileEnumerator() {
72868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  if (find_handle_ != INVALID_HANDLE_VALUE)
73868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    FindClose(find_handle_);
74868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}
75868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
76868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)FileEnumerator::FileInfo FileEnumerator::GetInfo() const {
77868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  if (!has_find_data_) {
78868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    NOTREACHED();
79868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    return FileInfo();
80868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  }
81868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  FileInfo ret;
82868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  memcpy(&ret.find_data_, &find_data_, sizeof(find_data_));
83868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  return ret;
84868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}
85868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
86868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)FilePath FileEnumerator::Next() {
87868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  base::ThreadRestrictions::AssertIOAllowed();
88868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
89868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  while (has_find_data_ || !pending_paths_.empty()) {
90868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    if (!has_find_data_) {
91868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      // The last find FindFirstFile operation is done, prepare a new one.
92868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      root_path_ = pending_paths_.top();
93868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      pending_paths_.pop();
94868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
95868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      // Start a new find operation.
96868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      FilePath src = root_path_;
97868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
98868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      if (pattern_.empty())
99868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)        src = src.Append(L"*");  // No pattern = match everything.
100868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      else
101868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)        src = src.Append(pattern_);
102868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
1031e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      if (base::win::GetVersion() >= base::win::VERSION_WIN7) {
1041e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)        // Use a "large fetch" on newer Windows which should speed up large
1051e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)        // enumerations (we seldom abort in the middle).
1061e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)        find_handle_ = FindFirstFileEx(src.value().c_str(),
1071e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)                                       FindExInfoBasic,  // Omit short name.
1081e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)                                       &find_data_,
1091e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)                                       FindExSearchNameMatch,
1101e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)                                       NULL,
1111e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)                                       FIND_FIRST_EX_LARGE_FETCH);
1121e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      } else {
1131e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)        find_handle_ = FindFirstFile(src.value().c_str(), &find_data_);
1141e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      }
115868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      has_find_data_ = true;
116868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    } else {
117868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      // Search for the next file/directory.
118868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      if (!FindNextFile(find_handle_, &find_data_)) {
119868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)        FindClose(find_handle_);
120868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)        find_handle_ = INVALID_HANDLE_VALUE;
121868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      }
122868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    }
123868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
124868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    if (INVALID_HANDLE_VALUE == find_handle_) {
125868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      has_find_data_ = false;
126868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
127868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      // This is reached when we have finished a directory and are advancing to
128868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      // the next one in the queue. We applied the pattern (if any) to the files
129868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      // in the root search directory, but for those directories which were
130868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      // matched, we want to enumerate all files inside them. This will happen
131868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      // when the handle is empty.
132868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      pattern_ = FilePath::StringType();
133868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
134868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      continue;
135868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    }
136868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
137868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    FilePath cur_file(find_data_.cFileName);
138868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    if (ShouldSkip(cur_file))
139868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      continue;
140868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
141868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    // Construct the absolute filename.
142868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    cur_file = root_path_.Append(find_data_.cFileName);
143868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
144868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    if (find_data_.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
145868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      if (recursive_) {
146868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)        // If |cur_file| is a directory, and we are doing recursive searching,
147868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)        // add it to pending_paths_ so we scan it after we finish scanning this
1483551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        // directory. However, don't do recursion through reparse points or we
1493551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        // may end up with an infinite cycle.
1503551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        if (!(find_data_.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
1513551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)          pending_paths_.push(cur_file);
152868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      }
153868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      if (file_type_ & FileEnumerator::DIRECTORIES)
154868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)        return cur_file;
155868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    } else if (file_type_ & FileEnumerator::FILES) {
156868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      return cur_file;
157868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    }
158868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  }
159868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
160868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  return FilePath();
161868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}
162868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
163868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}  // namespace base
164