Home | History | Annotate | Download | only in files
      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 
     20 FileEnumerator::FileInfo::FileInfo() {
     21   memset(&stat_, 0, sizeof(stat_));
     22 }
     23 
     24 bool FileEnumerator::FileInfo::IsDirectory() const {
     25   return S_ISDIR(stat_.st_mode);
     26 }
     27 
     28 FilePath FileEnumerator::FileInfo::GetName() const {
     29   return filename_;
     30 }
     31 
     32 int64_t FileEnumerator::FileInfo::GetSize() const {
     33   return stat_.st_size;
     34 }
     35 
     36 base::Time FileEnumerator::FileInfo::GetLastModifiedTime() const {
     37   return base::Time::FromTimeT(stat_.st_mtime);
     38 }
     39 
     40 // FileEnumerator --------------------------------------------------------------
     41 
     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 
     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 
     73 FileEnumerator::~FileEnumerator() {
     74 }
     75 
     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 
    117 FileEnumerator::FileInfo FileEnumerator::GetInfo() const {
    118   return directory_entries_[current_directory_entry_];
    119 }
    120 
    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