• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
JoinPaths(const std::string & one,const std::string & other)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 
FileScanner(std::vector<std::string> root_directories,Delegate * delegate,uint32_t scan_interval_ms,uint32_t scan_steps)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 
FileScanner(std::vector<std::string> root_directories,Delegate * delegate)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 
Scan()58 void FileScanner::Scan() {
59   while (!Done())
60     Step();
61   delegate_->OnInodeScanDone();
62 }
Scan(base::TaskRunner * task_runner)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 
NextDirectory()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 
Step()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 
Steps(uint32_t n)145 void FileScanner::Steps(uint32_t n) {
146   for (uint32_t i = 0; i < n && !Done(); ++i)
147     Step();
148 }
149 
Done()150 bool FileScanner::Done() {
151   return !current_dir_handle_ && queue_.empty();
152 }
153 
154 FileScanner::Delegate::~Delegate() = default;
155 
156 }  // namespace perfetto
157