• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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