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