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 <dirent.h>
8 #include <errno.h>
9 #include <fnmatch.h>
10
11 #include "base/logging.h"
12 #include "base/threading/thread_restrictions.h"
13
14 namespace base {
15
16 // FileEnumerator::FileInfo ----------------------------------------------------
17
FileInfo()18 FileEnumerator::FileInfo::FileInfo() {
19 memset(&stat_, 0, sizeof(stat_));
20 }
21
IsDirectory() const22 bool FileEnumerator::FileInfo::IsDirectory() const {
23 return S_ISDIR(stat_.st_mode);
24 }
25
GetName() const26 FilePath FileEnumerator::FileInfo::GetName() const {
27 return filename_;
28 }
29
GetSize() const30 int64 FileEnumerator::FileInfo::GetSize() const {
31 return stat_.st_size;
32 }
33
GetLastModifiedTime() const34 base::Time FileEnumerator::FileInfo::GetLastModifiedTime() const {
35 return base::Time::FromTimeT(stat_.st_mtime);
36 }
37
38 // FileEnumerator --------------------------------------------------------------
39
FileEnumerator(const FilePath & root_path,bool recursive,int file_type)40 FileEnumerator::FileEnumerator(const FilePath& root_path,
41 bool recursive,
42 int file_type)
43 : current_directory_entry_(0),
44 root_path_(root_path),
45 recursive_(recursive),
46 file_type_(file_type) {
47 // INCLUDE_DOT_DOT must not be specified if recursive.
48 DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_)));
49 pending_paths_.push(root_path);
50 }
51
FileEnumerator(const FilePath & root_path,bool recursive,int file_type,const FilePath::StringType & pattern)52 FileEnumerator::FileEnumerator(const FilePath& root_path,
53 bool recursive,
54 int file_type,
55 const FilePath::StringType& pattern)
56 : current_directory_entry_(0),
57 root_path_(root_path),
58 recursive_(recursive),
59 file_type_(file_type),
60 pattern_(root_path.Append(pattern).value()) {
61 // INCLUDE_DOT_DOT must not be specified if recursive.
62 DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_)));
63 // The Windows version of this code appends the pattern to the root_path,
64 // potentially only matching against items in the top-most directory.
65 // Do the same here.
66 if (pattern.empty())
67 pattern_ = FilePath::StringType();
68 pending_paths_.push(root_path);
69 }
70
~FileEnumerator()71 FileEnumerator::~FileEnumerator() {
72 }
73
Next()74 FilePath FileEnumerator::Next() {
75 ++current_directory_entry_;
76
77 // While we've exhausted the entries in the current directory, do the next
78 while (current_directory_entry_ >= directory_entries_.size()) {
79 if (pending_paths_.empty())
80 return FilePath();
81
82 root_path_ = pending_paths_.top();
83 root_path_ = root_path_.StripTrailingSeparators();
84 pending_paths_.pop();
85
86 std::vector<FileInfo> entries;
87 if (!ReadDirectory(&entries, root_path_, file_type_ & SHOW_SYM_LINKS))
88 continue;
89
90 directory_entries_.clear();
91 current_directory_entry_ = 0;
92 for (std::vector<FileInfo>::const_iterator i = entries.begin();
93 i != entries.end(); ++i) {
94 FilePath full_path = root_path_.Append(i->filename_);
95 if (ShouldSkip(full_path))
96 continue;
97
98 if (pattern_.size() &&
99 fnmatch(pattern_.c_str(), full_path.value().c_str(), FNM_NOESCAPE))
100 continue;
101
102 if (recursive_ && S_ISDIR(i->stat_.st_mode))
103 pending_paths_.push(full_path);
104
105 if ((S_ISDIR(i->stat_.st_mode) && (file_type_ & DIRECTORIES)) ||
106 (!S_ISDIR(i->stat_.st_mode) && (file_type_ & FILES)))
107 directory_entries_.push_back(*i);
108 }
109 }
110
111 return root_path_.Append(
112 directory_entries_[current_directory_entry_].filename_);
113 }
114
GetInfo() const115 FileEnumerator::FileInfo FileEnumerator::GetInfo() const {
116 return directory_entries_[current_directory_entry_];
117 }
118
ReadDirectory(std::vector<FileInfo> * entries,const FilePath & source,bool show_links)119 bool FileEnumerator::ReadDirectory(std::vector<FileInfo>* entries,
120 const FilePath& source, bool show_links) {
121 base::ThreadRestrictions::AssertIOAllowed();
122 DIR* dir = opendir(source.value().c_str());
123 if (!dir)
124 return false;
125
126 #if !defined(OS_LINUX) && !defined(OS_MACOSX) && !defined(OS_BSD) && \
127 !defined(OS_SOLARIS) && !defined(OS_ANDROID)
128 #error Port warning: depending on the definition of struct dirent, \
129 additional space for pathname may be needed
130 #endif
131
132 struct dirent dent_buf;
133 struct dirent* dent;
134 while (readdir_r(dir, &dent_buf, &dent) == 0 && dent) {
135 FileInfo info;
136 info.filename_ = FilePath(dent->d_name);
137
138 FilePath full_name = source.Append(dent->d_name);
139 int ret;
140 if (show_links)
141 ret = lstat(full_name.value().c_str(), &info.stat_);
142 else
143 ret = stat(full_name.value().c_str(), &info.stat_);
144 if (ret < 0) {
145 // Print the stat() error message unless it was ENOENT and we're
146 // following symlinks.
147 if (!(errno == ENOENT && !show_links)) {
148 DPLOG(ERROR) << "Couldn't stat "
149 << source.Append(dent->d_name).value();
150 }
151 memset(&info.stat_, 0, sizeof(info.stat_));
152 }
153 entries->push_back(info);
154 }
155
156 closedir(dir);
157 return true;
158 }
159
160 } // namespace base
161