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 26namespace perfetto { 27namespace { 28 29std::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 41FileScanner::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 51FileScanner::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 58void FileScanner::Scan() { 59 while (!Done()) 60 Step(); 61 delegate_->OnInodeScanDone(); 62} 63void 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 78void 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 105void 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 145void FileScanner::Steps(uint32_t n) { 146 for (uint32_t i = 0; i < n && !Done(); ++i) 147 Step(); 148} 149 150bool FileScanner::Done() { 151 return !current_dir_handle_ && queue_.empty(); 152} 153 154FileScanner::Delegate::~Delegate() = default; 155 156} // namespace perfetto 157