1868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)// Copyright (c) 2013 The Chromium Authors. All rights reserved.
2868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
3868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)// found in the LICENSE file.
4868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
5868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/files/file_enumerator.h"
6868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
7868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include <dirent.h>
8868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include <errno.h>
9868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include <fnmatch.h>
10868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
11868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/logging.h"
12868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/threading/thread_restrictions.h"
13868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
14868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)namespace base {
15868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
16868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)// FileEnumerator::FileInfo ----------------------------------------------------
17868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
18868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)FileEnumerator::FileInfo::FileInfo() {
19868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  memset(&stat_, 0, sizeof(stat_));
20868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}
21868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
22868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)bool FileEnumerator::FileInfo::IsDirectory() const {
23868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  return S_ISDIR(stat_.st_mode);
24868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}
25868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
26868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)FilePath FileEnumerator::FileInfo::GetName() const {
27868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  return filename_;
28868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}
29868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
30868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)int64 FileEnumerator::FileInfo::GetSize() const {
31868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  return stat_.st_size;
32868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}
33868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
34868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)base::Time FileEnumerator::FileInfo::GetLastModifiedTime() const {
35868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  return base::Time::FromTimeT(stat_.st_mtime);
36868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}
37868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
38868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)// FileEnumerator --------------------------------------------------------------
39868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
40868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)FileEnumerator::FileEnumerator(const FilePath& root_path,
41868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)                               bool recursive,
42868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)                               int file_type)
43868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    : current_directory_entry_(0),
44868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      root_path_(root_path),
45868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      recursive_(recursive),
46868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      file_type_(file_type) {
47868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  // INCLUDE_DOT_DOT must not be specified if recursive.
48868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_)));
49868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  pending_paths_.push(root_path);
50868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}
51868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
52868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)FileEnumerator::FileEnumerator(const FilePath& root_path,
53868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)                               bool recursive,
54868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)                               int file_type,
55868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)                               const FilePath::StringType& pattern)
56868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    : current_directory_entry_(0),
57868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      root_path_(root_path),
58868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      recursive_(recursive),
59868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      file_type_(file_type),
60868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      pattern_(root_path.Append(pattern).value()) {
61868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  // INCLUDE_DOT_DOT must not be specified if recursive.
62868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_)));
63868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  // The Windows version of this code appends the pattern to the root_path,
64868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  // potentially only matching against items in the top-most directory.
65868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  // Do the same here.
66868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  if (pattern.empty())
67868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    pattern_ = FilePath::StringType();
68868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  pending_paths_.push(root_path);
69868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}
70868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
71868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)FileEnumerator::~FileEnumerator() {
72868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}
73868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
74868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)FilePath FileEnumerator::Next() {
75868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  ++current_directory_entry_;
76868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
77868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  // While we've exhausted the entries in the current directory, do the next
78868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  while (current_directory_entry_ >= directory_entries_.size()) {
79868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    if (pending_paths_.empty())
80868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      return FilePath();
81868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
82868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    root_path_ = pending_paths_.top();
83868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    root_path_ = root_path_.StripTrailingSeparators();
84868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    pending_paths_.pop();
85868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
86868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    std::vector<FileInfo> entries;
87868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    if (!ReadDirectory(&entries, root_path_, file_type_ & SHOW_SYM_LINKS))
88868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      continue;
89868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
90868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    directory_entries_.clear();
91868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    current_directory_entry_ = 0;
92868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    for (std::vector<FileInfo>::const_iterator i = entries.begin();
93868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)         i != entries.end(); ++i) {
94868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      FilePath full_path = root_path_.Append(i->filename_);
95868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      if (ShouldSkip(full_path))
96868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)        continue;
97868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
98868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      if (pattern_.size() &&
99868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)          fnmatch(pattern_.c_str(), full_path.value().c_str(), FNM_NOESCAPE))
100868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)        continue;
101868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
102868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      if (recursive_ && S_ISDIR(i->stat_.st_mode))
103868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)        pending_paths_.push(full_path);
104868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
105868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      if ((S_ISDIR(i->stat_.st_mode) && (file_type_ & DIRECTORIES)) ||
106868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)          (!S_ISDIR(i->stat_.st_mode) && (file_type_ & FILES)))
107868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)        directory_entries_.push_back(*i);
108868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    }
109868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  }
110868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
111868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  return root_path_.Append(
112868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      directory_entries_[current_directory_entry_].filename_);
113868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}
114868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
115868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)FileEnumerator::FileInfo FileEnumerator::GetInfo() const {
116868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  return directory_entries_[current_directory_entry_];
117868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}
118868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
119868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)bool FileEnumerator::ReadDirectory(std::vector<FileInfo>* entries,
120868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)                                   const FilePath& source, bool show_links) {
121868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  base::ThreadRestrictions::AssertIOAllowed();
122868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  DIR* dir = opendir(source.value().c_str());
123868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  if (!dir)
124868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    return false;
125868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
126868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#if !defined(OS_LINUX) && !defined(OS_MACOSX) && !defined(OS_BSD) && \
127868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    !defined(OS_SOLARIS) && !defined(OS_ANDROID)
128868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  #error Port warning: depending on the definition of struct dirent, \
129868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)         additional space for pathname may be needed
130868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#endif
131868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
132868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  struct dirent dent_buf;
133868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  struct dirent* dent;
134868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  while (readdir_r(dir, &dent_buf, &dent) == 0 && dent) {
135868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    FileInfo info;
136868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    info.filename_ = FilePath(dent->d_name);
137868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
138868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    FilePath full_name = source.Append(dent->d_name);
139868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    int ret;
140868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    if (show_links)
141868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      ret = lstat(full_name.value().c_str(), &info.stat_);
142868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    else
143868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      ret = stat(full_name.value().c_str(), &info.stat_);
144868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    if (ret < 0) {
145868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      // Print the stat() error message unless it was ENOENT and we're
146868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      // following symlinks.
147868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      if (!(errno == ENOENT && !show_links)) {
148868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)        DPLOG(ERROR) << "Couldn't stat "
149868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)                     << source.Append(dent->d_name).value();
150868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      }
151868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      memset(&info.stat_, 0, sizeof(info.stat_));
152868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    }
153868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    entries->push_back(info);
154868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  }
155868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
156868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  closedir(dir);
157868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  return true;
158868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}
159868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
160868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}  // namespace base
161