Home | History | Annotate | Download | only in filesystem
      1 /*
      2  * Copyright (C) 2018 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include "src/traced/probes/filesystem/file_scanner.h"
     18 
     19 #include <dirent.h>
     20 #include <sys/stat.h>
     21 #include <sys/types.h>
     22 #include <unistd.h>
     23 
     24 #include "src/traced/probes/filesystem/inode_file_data_source.h"
     25 
     26 namespace perfetto {
     27 namespace {
     28 
     29 std::string JoinPaths(const std::string& one, const std::string& other) {
     30   std::string result;
     31   result.reserve(one.size() + other.size() + 1);
     32   result += one;
     33   if (!result.empty() && result.back() != '/')
     34     result += '/';
     35   result += other;
     36   return result;
     37 }
     38 
     39 }  // namespace
     40 
     41 FileScanner::FileScanner(std::vector<std::string> root_directories,
     42                          Delegate* delegate,
     43                          uint32_t scan_interval_ms,
     44                          uint32_t scan_steps)
     45     : delegate_(delegate),
     46       scan_interval_ms_(scan_interval_ms),
     47       scan_steps_(scan_steps),
     48       queue_(std::move(root_directories)),
     49       weak_factory_(this) {}
     50 
     51 FileScanner::FileScanner(std::vector<std::string> root_directories,
     52                          Delegate* delegate)
     53     : FileScanner(std::move(root_directories),
     54                   delegate,
     55                   0 /* scan_interval_ms */,
     56                   0 /* scan_steps */) {}
     57 
     58 void FileScanner::Scan() {
     59   while (!Done())
     60     Step();
     61   delegate_->OnInodeScanDone();
     62 }
     63 void FileScanner::Scan(base::TaskRunner* task_runner) {
     64   PERFETTO_DCHECK(scan_interval_ms_ && scan_steps_);
     65   Steps(scan_steps_);
     66   if (Done())
     67     return delegate_->OnInodeScanDone();
     68   auto weak_this = weak_factory_.GetWeakPtr();
     69   task_runner->PostDelayedTask(
     70       [weak_this, task_runner] {
     71         if (!weak_this)
     72           return;
     73         weak_this->Scan(task_runner);
     74       },
     75       scan_interval_ms_);
     76 }
     77 
     78 void FileScanner::NextDirectory() {
     79   std::string directory = std::move(queue_.back());
     80   queue_.pop_back();
     81   current_dir_handle_.reset(opendir(directory.c_str()));
     82   if (!current_dir_handle_) {
     83     PERFETTO_DPLOG("opendir %s", directory.c_str());
     84     current_directory_.clear();
     85     return;
     86   }
     87   current_directory_ = std::move(directory);
     88 
     89   struct stat buf;
     90   if (fstat(dirfd(current_dir_handle_.get()), &buf) != 0) {
     91     PERFETTO_DPLOG("fstat %s", current_directory_.c_str());
     92     current_dir_handle_.reset();
     93     current_directory_.clear();
     94     return;
     95   }
     96 
     97   if (S_ISLNK(buf.st_mode)) {
     98     current_dir_handle_.reset();
     99     current_directory_.clear();
    100     return;
    101   }
    102   current_block_device_id_ = buf.st_dev;
    103 }
    104 
    105 void FileScanner::Step() {
    106   if (!current_dir_handle_) {
    107     if (queue_.empty())
    108       return;
    109     NextDirectory();
    110   }
    111 
    112   if (!current_dir_handle_)
    113     return;
    114 
    115   struct dirent* entry = readdir(current_dir_handle_.get());
    116   if (entry == nullptr) {
    117     current_dir_handle_.reset();
    118     return;
    119   }
    120 
    121   std::string filename = entry->d_name;
    122   if (filename == "." || filename == "..")
    123     return;
    124 
    125   std::string filepath = JoinPaths(current_directory_, filename);
    126 
    127   protos::pbzero::InodeFileMap_Entry_Type type =
    128       protos::pbzero::InodeFileMap_Entry_Type_UNKNOWN;
    129   // Readdir and stat not guaranteed to have directory info for all systems
    130   if (entry->d_type == DT_DIR) {
    131     // Continue iterating through files if current entry is a directory
    132     queue_.emplace_back(filepath);
    133     type = protos::pbzero::InodeFileMap_Entry_Type_DIRECTORY;
    134   } else if (entry->d_type == DT_REG) {
    135     type = protos::pbzero::InodeFileMap_Entry_Type_FILE;
    136   }
    137 
    138   if (!delegate_->OnInodeFound(current_block_device_id_, entry->d_ino, filepath,
    139                                type)) {
    140     queue_.clear();
    141     current_dir_handle_.reset();
    142   }
    143 }
    144 
    145 void FileScanner::Steps(uint32_t n) {
    146   for (uint32_t i = 0; i < n && !Done(); ++i)
    147     Step();
    148 }
    149 
    150 bool FileScanner::Done() {
    151   return !current_dir_handle_ && queue_.empty();
    152 }
    153 
    154 FileScanner::Delegate::~Delegate() = default;
    155 
    156 }  // namespace perfetto
    157