• 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 
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