blob: 0b6e7cc75f0256469f628124a14daa8bdb492c75 [file] [log] [blame]
[email protected]25a4c1c2013-06-08 04:53:361// 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 <dirent.h>
8#include <errno.h>
9#include <fnmatch.h>
avi543540e2015-12-24 05:15:3210#include <stdint.h>
ivafanas829f2e02017-07-02 09:01:4411#include <string.h>
[email protected]25a4c1c2013-06-08 04:53:3612
13#include "base/logging.h"
Etienne Pierre-Doray3879b052018-09-17 14:17:2214#include "base/threading/scoped_blocking_call.h"
avi543540e2015-12-24 05:15:3215#include "build/build_config.h"
[email protected]25a4c1c2013-06-08 04:53:3616
17namespace base {
ivafanas829f2e02017-07-02 09:01:4418namespace {
19
20void GetStat(const FilePath& path, bool show_links, struct stat* st) {
21 DCHECK(st);
22 const int res = show_links ? lstat(path.value().c_str(), st)
23 : stat(path.value().c_str(), st);
24 if (res < 0) {
25 // Print the stat() error message unless it was ENOENT and we're following
26 // symlinks.
27 if (!(errno == ENOENT && !show_links))
28 DPLOG(ERROR) << "Couldn't stat" << path.value();
29 memset(st, 0, sizeof(*st));
30 }
31}
32
33} // namespace
[email protected]25a4c1c2013-06-08 04:53:3634
35// FileEnumerator::FileInfo ----------------------------------------------------
36
37FileEnumerator::FileInfo::FileInfo() {
38 memset(&stat_, 0, sizeof(stat_));
39}
40
41bool FileEnumerator::FileInfo::IsDirectory() const {
42 return S_ISDIR(stat_.st_mode);
43}
44
45FilePath FileEnumerator::FileInfo::GetName() const {
46 return filename_;
47}
48
avi543540e2015-12-24 05:15:3249int64_t FileEnumerator::FileInfo::GetSize() const {
[email protected]25a4c1c2013-06-08 04:53:3650 return stat_.st_size;
51}
52
53base::Time FileEnumerator::FileInfo::GetLastModifiedTime() const {
54 return base::Time::FromTimeT(stat_.st_mtime);
55}
56
57// FileEnumerator --------------------------------------------------------------
58
59FileEnumerator::FileEnumerator(const FilePath& root_path,
60 bool recursive,
61 int file_type)
ivafanas829f2e02017-07-02 09:01:4462 : FileEnumerator(root_path,
63 recursive,
64 file_type,
65 FilePath::StringType(),
66 FolderSearchPolicy::MATCH_ONLY) {}
[email protected]25a4c1c2013-06-08 04:53:3667
68FileEnumerator::FileEnumerator(const FilePath& root_path,
69 bool recursive,
70 int file_type,
71 const FilePath::StringType& pattern)
ivafanas829f2e02017-07-02 09:01:4472 : FileEnumerator(root_path,
73 recursive,
74 file_type,
75 pattern,
76 FolderSearchPolicy::MATCH_ONLY) {}
77
78FileEnumerator::FileEnumerator(const FilePath& root_path,
79 bool recursive,
80 int file_type,
81 const FilePath::StringType& pattern,
82 FolderSearchPolicy folder_search_policy)
[email protected]25a4c1c2013-06-08 04:53:3683 : current_directory_entry_(0),
84 root_path_(root_path),
85 recursive_(recursive),
86 file_type_(file_type),
ivafanas829f2e02017-07-02 09:01:4487 pattern_(pattern),
88 folder_search_policy_(folder_search_policy) {
[email protected]25a4c1c2013-06-08 04:53:3689 // INCLUDE_DOT_DOT must not be specified if recursive.
90 DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_)));
ivafanas829f2e02017-07-02 09:01:4491
Alexei Filippovec7fdd02018-09-13 04:16:4292 if (recursive && !(file_type & SHOW_SYM_LINKS)) {
93 struct stat st;
94 GetStat(root_path, false, &st);
95 visited_directories_.insert(st.st_ino);
96 }
97
[email protected]25a4c1c2013-06-08 04:53:3698 pending_paths_.push(root_path);
99}
100
Chris Watkinsbb7211c2017-11-29 07:16:38101FileEnumerator::~FileEnumerator() = default;
[email protected]25a4c1c2013-06-08 04:53:36102
103FilePath FileEnumerator::Next() {
Etienne Pierre-Doray3879b052018-09-17 14:17:22104 ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
ivafanas829f2e02017-07-02 09:01:44105
[email protected]25a4c1c2013-06-08 04:53:36106 ++current_directory_entry_;
107
108 // While we've exhausted the entries in the current directory, do the next
109 while (current_directory_entry_ >= directory_entries_.size()) {
110 if (pending_paths_.empty())
111 return FilePath();
112
113 root_path_ = pending_paths_.top();
114 root_path_ = root_path_.StripTrailingSeparators();
115 pending_paths_.pop();
116
ivafanas829f2e02017-07-02 09:01:44117 DIR* dir = opendir(root_path_.value().c_str());
118 if (!dir)
[email protected]25a4c1c2013-06-08 04:53:36119 continue;
120
121 directory_entries_.clear();
ivafanas829f2e02017-07-02 09:01:44122
123#if defined(OS_FUCHSIA)
124 // Fuchsia does not support .. on the file system server side, see
125 // https://fuchsia.googlesource.com/docs/+/master/dotdot.md and
126 // https://crbug.com/735540. However, for UI purposes, having the parent
127 // directory show up in directory listings makes sense, so we add it here to
128 // match the expectation on other operating systems. In cases where this
129 // is useful it should be resolvable locally.
130 FileInfo dotdot;
131 dotdot.stat_.st_mode = S_IFDIR;
132 dotdot.filename_ = FilePath("..");
Kevin Marshallac4b41c2017-07-11 18:20:47133 if (!ShouldSkip(dotdot.filename_)) {
134 directory_entries_.push_back(std::move(dotdot));
135 }
ivafanas829f2e02017-07-02 09:01:44136#endif // OS_FUCHSIA
137
[email protected]25a4c1c2013-06-08 04:53:36138 current_directory_entry_ = 0;
ivafanas829f2e02017-07-02 09:01:44139 struct dirent* dent;
140 while ((dent = readdir(dir))) {
141 FileInfo info;
142 info.filename_ = FilePath(dent->d_name);
143
144 if (ShouldSkip(info.filename_))
[email protected]25a4c1c2013-06-08 04:53:36145 continue;
146
ivafanas829f2e02017-07-02 09:01:44147 const bool is_pattern_matched = IsPatternMatched(info.filename_);
148
149 // MATCH_ONLY policy enumerates files and directories which matching
150 // pattern only. So we can early skip further checks.
151 if (folder_search_policy_ == FolderSearchPolicy::MATCH_ONLY &&
152 !is_pattern_matched)
[email protected]25a4c1c2013-06-08 04:53:36153 continue;
154
ivafanas829f2e02017-07-02 09:01:44155 // Do not call OS stat/lstat if there is no sense to do it. If pattern is
156 // not matched (file will not appear in results) and search is not
157 // recursive (possible directory will not be added to pending paths) -
158 // there is no sense to obtain item below.
159 if (!recursive_ && !is_pattern_matched)
160 continue;
161
162 const FilePath full_path = root_path_.Append(info.filename_);
Alexei Filippovec7fdd02018-09-13 04:16:42163 const bool show_sym_links = file_type_ & SHOW_SYM_LINKS;
164 GetStat(full_path, show_sym_links, &info.stat_);
ivafanas829f2e02017-07-02 09:01:44165
Thiago Farina16aacd92017-07-27 15:43:53166 const bool is_dir = info.IsDirectory();
ivafanas829f2e02017-07-02 09:01:44167
Alexei Filippovec7fdd02018-09-13 04:16:42168 // Recursive mode: schedule traversal of a directory if either
169 // SHOW_SYM_LINKS is on or we haven't visited the directory yet.
170 if (recursive_ && is_dir &&
171 (show_sym_links ||
172 visited_directories_.insert(info.stat_.st_ino).second)) {
[email protected]25a4c1c2013-06-08 04:53:36173 pending_paths_.push(full_path);
Alexei Filippovec7fdd02018-09-13 04:16:42174 }
[email protected]25a4c1c2013-06-08 04:53:36175
ivafanas829f2e02017-07-02 09:01:44176 if (is_pattern_matched && IsTypeMatched(is_dir))
177 directory_entries_.push_back(std::move(info));
[email protected]25a4c1c2013-06-08 04:53:36178 }
ivafanas829f2e02017-07-02 09:01:44179 closedir(dir);
180
181 // MATCH_ONLY policy enumerates files in matched subfolders by "*" pattern.
182 // ALL policy enumerates files in all subfolders by origin pattern.
183 if (folder_search_policy_ == FolderSearchPolicy::MATCH_ONLY)
184 pattern_.clear();
[email protected]25a4c1c2013-06-08 04:53:36185 }
186
187 return root_path_.Append(
188 directory_entries_[current_directory_entry_].filename_);
189}
190
191FileEnumerator::FileInfo FileEnumerator::GetInfo() const {
192 return directory_entries_[current_directory_entry_];
193}
194
ivafanas829f2e02017-07-02 09:01:44195bool FileEnumerator::IsPatternMatched(const FilePath& path) const {
196 return pattern_.empty() ||
197 !fnmatch(pattern_.c_str(), path.value().c_str(), FNM_NOESCAPE);
[email protected]25a4c1c2013-06-08 04:53:36198}
199
200} // namespace base