Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(390)

Side by Side Diff: base/files/file_enumerator_posix.cc

Issue 2892173003: Add recursive pattern matching for subfolders in file_enumerator. (Closed)
Patch Set: Add recursive pattern matching for subfolders in file_enumerator Created 3 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « base/files/file_enumerator.cc ('k') | base/files/file_enumerator_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "base/files/file_enumerator.h" 5 #include "base/files/file_enumerator.h"
6 6
7 #include <dirent.h> 7 #include <dirent.h>
8 #include <errno.h> 8 #include <errno.h>
9 #include <fnmatch.h> 9 #include <fnmatch.h>
10 #include <stdint.h> 10 #include <stdint.h>
11 #include <string.h>
11 12
12 #include "base/logging.h" 13 #include "base/logging.h"
13 #include "base/threading/thread_restrictions.h" 14 #include "base/threading/thread_restrictions.h"
14 #include "build/build_config.h" 15 #include "build/build_config.h"
15 16
16 namespace base { 17 namespace base {
18 namespace {
19
20 void 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
17 34
18 // FileEnumerator::FileInfo ---------------------------------------------------- 35 // FileEnumerator::FileInfo ----------------------------------------------------
19 36
20 FileEnumerator::FileInfo::FileInfo() { 37 FileEnumerator::FileInfo::FileInfo() {
21 memset(&stat_, 0, sizeof(stat_)); 38 memset(&stat_, 0, sizeof(stat_));
22 } 39 }
23 40
24 bool FileEnumerator::FileInfo::IsDirectory() const { 41 bool FileEnumerator::FileInfo::IsDirectory() const {
25 return S_ISDIR(stat_.st_mode); 42 return S_ISDIR(stat_.st_mode);
26 } 43 }
27 44
28 FilePath FileEnumerator::FileInfo::GetName() const { 45 FilePath FileEnumerator::FileInfo::GetName() const {
29 return filename_; 46 return filename_;
30 } 47 }
31 48
32 int64_t FileEnumerator::FileInfo::GetSize() const { 49 int64_t FileEnumerator::FileInfo::GetSize() const {
33 return stat_.st_size; 50 return stat_.st_size;
34 } 51 }
35 52
36 base::Time FileEnumerator::FileInfo::GetLastModifiedTime() const { 53 base::Time FileEnumerator::FileInfo::GetLastModifiedTime() const {
37 return base::Time::FromTimeT(stat_.st_mtime); 54 return base::Time::FromTimeT(stat_.st_mtime);
38 } 55 }
39 56
40 // FileEnumerator -------------------------------------------------------------- 57 // FileEnumerator --------------------------------------------------------------
41 58
42 FileEnumerator::FileEnumerator(const FilePath& root_path, 59 FileEnumerator::FileEnumerator(const FilePath& root_path,
43 bool recursive, 60 bool recursive,
44 int file_type) 61 int file_type)
45 : current_directory_entry_(0), 62 : FileEnumerator(root_path,
46 root_path_(root_path), 63 recursive,
47 recursive_(recursive), 64 file_type,
48 file_type_(file_type) { 65 FilePath::StringType(),
49 // INCLUDE_DOT_DOT must not be specified if recursive. 66 FolderSearchPolicy::MATCH_ONLY) {}
50 DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_)));
51 pending_paths_.push(root_path);
52 }
53 67
54 FileEnumerator::FileEnumerator(const FilePath& root_path, 68 FileEnumerator::FileEnumerator(const FilePath& root_path,
55 bool recursive, 69 bool recursive,
56 int file_type, 70 int file_type,
57 const FilePath::StringType& pattern) 71 const FilePath::StringType& pattern)
72 : FileEnumerator(root_path,
73 recursive,
74 file_type,
75 pattern,
76 FolderSearchPolicy::MATCH_ONLY) {}
77
78 FileEnumerator::FileEnumerator(const FilePath& root_path,
79 bool recursive,
80 int file_type,
81 const FilePath::StringType& pattern,
82 FolderSearchPolicy folder_search_policy)
58 : current_directory_entry_(0), 83 : current_directory_entry_(0),
59 root_path_(root_path), 84 root_path_(root_path),
60 recursive_(recursive), 85 recursive_(recursive),
61 file_type_(file_type), 86 file_type_(file_type),
62 pattern_(root_path.Append(pattern).value()) { 87 pattern_(pattern),
88 folder_search_policy_(folder_search_policy) {
63 // INCLUDE_DOT_DOT must not be specified if recursive. 89 // INCLUDE_DOT_DOT must not be specified if recursive.
64 DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_))); 90 DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_)));
65 // The Windows version of this code appends the pattern to the root_path, 91
66 // potentially only matching against items in the top-most directory.
67 // Do the same here.
68 if (pattern.empty())
69 pattern_ = FilePath::StringType();
70 pending_paths_.push(root_path); 92 pending_paths_.push(root_path);
71 } 93 }
72 94
73 FileEnumerator::~FileEnumerator() { 95 FileEnumerator::~FileEnumerator() {
74 } 96 }
75 97
76 FilePath FileEnumerator::Next() { 98 FilePath FileEnumerator::Next() {
99 base::ThreadRestrictions::AssertIOAllowed();
100
77 ++current_directory_entry_; 101 ++current_directory_entry_;
78 102
79 // While we've exhausted the entries in the current directory, do the next 103 // While we've exhausted the entries in the current directory, do the next
80 while (current_directory_entry_ >= directory_entries_.size()) { 104 while (current_directory_entry_ >= directory_entries_.size()) {
81 if (pending_paths_.empty()) 105 if (pending_paths_.empty())
82 return FilePath(); 106 return FilePath();
83 107
84 root_path_ = pending_paths_.top(); 108 root_path_ = pending_paths_.top();
85 root_path_ = root_path_.StripTrailingSeparators(); 109 root_path_ = root_path_.StripTrailingSeparators();
86 pending_paths_.pop(); 110 pending_paths_.pop();
87 111
88 std::vector<FileInfo> entries; 112 DIR* dir = opendir(root_path_.value().c_str());
89 if (!ReadDirectory(&entries, root_path_, file_type_ & SHOW_SYM_LINKS)) 113 if (!dir)
90 continue; 114 continue;
91 115
92 directory_entries_.clear(); 116 directory_entries_.clear();
117
118 #if defined(OS_FUCHSIA)
119 // Fuchsia does not support .. on the file system server side, see
120 // https://fuchsia.googlesource.com/docs/+/master/dotdot.md and
121 // https://crbug.com/735540. However, for UI purposes, having the parent
122 // directory show up in directory listings makes sense, so we add it here to
123 // match the expectation on other operating systems. In cases where this
124 // is useful it should be resolvable locally.
125 FileInfo dotdot;
126 dotdot.stat_.st_mode = S_IFDIR;
127 dotdot.filename_ = FilePath("..");
128 directory_entries_->push_back(dotdot);
129 #endif // OS_FUCHSIA
130
93 current_directory_entry_ = 0; 131 current_directory_entry_ = 0;
94 for (std::vector<FileInfo>::const_iterator i = entries.begin(); 132 struct dirent* dent;
95 i != entries.end(); ++i) { 133 while ((dent = readdir(dir))) {
96 FilePath full_path = root_path_.Append(i->filename_); 134 FileInfo info;
97 if (ShouldSkip(full_path)) 135 info.filename_ = FilePath(dent->d_name);
136
137 if (ShouldSkip(info.filename_))
98 continue; 138 continue;
99 139
100 if (pattern_.size() && 140 const bool is_pattern_matched = IsPatternMatched(info.filename_);
101 fnmatch(pattern_.c_str(), full_path.value().c_str(), FNM_NOESCAPE)) 141
142 // MATCH_ONLY policy enumerates files and directories which matching
143 // pattern only. So we can early skip further checks.
144 if (folder_search_policy_ == FolderSearchPolicy::MATCH_ONLY &&
145 !is_pattern_matched)
102 continue; 146 continue;
103 147
104 if (recursive_ && S_ISDIR(i->stat_.st_mode)) 148 // Do not call OS stat/lstat if there is no sense to do it. If pattern is
149 // not matched (file will not appear in results) and search is not
150 // recursive (possible directory will not be added to pending paths) -
151 // there is no sense to obtain item below.
152 if (!recursive_ && !is_pattern_matched)
153 continue;
154
155 const FilePath full_path = root_path_.Append(info.filename_);
156 GetStat(full_path, file_type_ & SHOW_SYM_LINKS, &info.stat_);
157
158 const bool is_dir = S_ISDIR(info.stat_.st_mode);
159
160 if (recursive_ && is_dir)
105 pending_paths_.push(full_path); 161 pending_paths_.push(full_path);
106 162
107 if ((S_ISDIR(i->stat_.st_mode) && (file_type_ & DIRECTORIES)) || 163 if (is_pattern_matched && IsTypeMatched(is_dir))
108 (!S_ISDIR(i->stat_.st_mode) && (file_type_ & FILES))) 164 directory_entries_.push_back(std::move(info));
109 directory_entries_.push_back(*i);
110 } 165 }
166 closedir(dir);
167
168 // MATCH_ONLY policy enumerates files in matched subfolders by "*" pattern.
169 // ALL policy enumerates files in all subfolders by origin pattern.
170 if (folder_search_policy_ == FolderSearchPolicy::MATCH_ONLY)
171 pattern_.clear();
111 } 172 }
112 173
113 return root_path_.Append( 174 return root_path_.Append(
114 directory_entries_[current_directory_entry_].filename_); 175 directory_entries_[current_directory_entry_].filename_);
115 } 176 }
116 177
117 FileEnumerator::FileInfo FileEnumerator::GetInfo() const { 178 FileEnumerator::FileInfo FileEnumerator::GetInfo() const {
118 return directory_entries_[current_directory_entry_]; 179 return directory_entries_[current_directory_entry_];
119 } 180 }
120 181
121 bool FileEnumerator::ReadDirectory(std::vector<FileInfo>* entries, 182 bool FileEnumerator::IsPatternMatched(const FilePath& path) const {
122 const FilePath& source, bool show_links) { 183 return pattern_.empty() ||
123 base::ThreadRestrictions::AssertIOAllowed(); 184 !fnmatch(pattern_.c_str(), path.value().c_str(), FNM_NOESCAPE);
124 DIR* dir = opendir(source.value().c_str());
125 if (!dir)
126 return false;
127
128 #if defined(OS_FUCHSIA)
129 // Fuchsia does not support .. on the file system server side, see
130 // https://fuchsia.googlesource.com/docs/+/master/dotdot.md and
131 // https://crbug.com/735540. However, for UI purposes, having the parent
132 // directory show up in directory listings makes sense, so we add it here to
133 // match the expectation on other operating systems. In cases where this
134 // is useful it should be resolvable locally.
135 FileInfo dotdot;
136 dotdot.stat_.st_mode = S_IFDIR;
137 dotdot.filename_ = FilePath("..");
138 entries->push_back(dotdot);
139 #endif // OS_FUCHSIA
140
141 struct dirent* dent;
142 while ((dent = readdir(dir))) {
143 FileInfo info;
144 info.filename_ = FilePath(dent->d_name);
145
146 FilePath full_name = source.Append(dent->d_name);
147 int ret;
148 if (show_links)
149 ret = lstat(full_name.value().c_str(), &info.stat_);
150 else
151 ret = stat(full_name.value().c_str(), &info.stat_);
152 if (ret < 0) {
153 // Print the stat() error message unless it was ENOENT and we're
154 // following symlinks.
155 if (!(errno == ENOENT && !show_links)) {
156 DPLOG(ERROR) << "Couldn't stat "
157 << source.Append(dent->d_name).value();
158 }
159 memset(&info.stat_, 0, sizeof(info.stat_));
160 }
161 entries->push_back(info);
162 }
163
164 closedir(dir);
165 return true;
166 } 185 }
167 186
168 } // namespace base 187 } // namespace base
OLDNEW
« no previous file with comments | « base/files/file_enumerator.cc ('k') | base/files/file_enumerator_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698