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 
     11 #include "base/logging.h"
     12 #include "base/threading/thread_restrictions.h"
     13 
     14 namespace base {
     15 
     16 // FileEnumerator::FileInfo ----------------------------------------------------
     17 
     18 FileEnumerator::FileInfo::FileInfo() {
     19   memset(&stat_, 0, sizeof(stat_));
     20 }
     21 
     22 bool FileEnumerator::FileInfo::IsDirectory() const {
     23   return S_ISDIR(stat_.st_mode);
     24 }
     25 
     26 FilePath FileEnumerator::FileInfo::GetName() const {
     27   return filename_;
     28 }
     29 
     30 int64 FileEnumerator::FileInfo::GetSize() const {
     31   return stat_.st_size;
     32 }
     33 
     34 base::Time FileEnumerator::FileInfo::GetLastModifiedTime() const {
     35   return base::Time::FromTimeT(stat_.st_mtime);
     36 }
     37 
     38 // FileEnumerator --------------------------------------------------------------
     39 
     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 
     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 
     71 FileEnumerator::~FileEnumerator() {
     72 }
     73 
     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 
    115 FileEnumerator::FileInfo FileEnumerator::GetInfo() const {
    116   return directory_entries_[current_directory_entry_];
    117 }
    118 
    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