• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "io/dev/file_monitor.h"
17 
18 #include <algorithm>
19 #include <cstddef>
20 
21 #include <base/containers/iterator.h>
22 #include <base/containers/string.h>
23 #include <base/containers/string_view.h>
24 #include <base/containers/type_traits.h>
25 #include <base/containers/unique_ptr.h>
26 #include <base/containers/unordered_map.h>
27 #include <base/containers/vector.h>
28 #include <base/namespace.h>
29 #include <core/io/intf_directory.h>
30 #include <core/io/intf_file_manager.h>
31 #include <core/namespace.h>
32 #include <core/perf/cpu_perf_scope.h>
33 
34 CORE_BEGIN_NAMESPACE()
35 using BASE_NS::string;
36 using BASE_NS::string_view;
37 using BASE_NS::vector;
38 
RecursivelyCollectAllFiles(string & path)39 void FileMonitor::RecursivelyCollectAllFiles(string& path)
40 {
41     auto dir = fileManager_.OpenDirectory(path);
42     if (dir == nullptr) {
43         // path does not exist.
44         return;
45     }
46     const auto& entries = dir->GetEntries();
47     const size_t oldLength = path.length();
48     for (const IDirectory::Entry& entry : entries) {
49         if (entry.name == "." || entry.name == "..") {
50             continue;
51         }
52 
53         path.reserve(oldLength + entry.name.length() + 1);
54         path += entry.name;
55 
56         if (entry.type == IDirectory::Entry::DIRECTORY) {
57             path += '/';
58         }
59 
60         auto iterator = files_.find(path);
61         if (iterator != files_.end()) {
62             // File or directory being tracked, see if it is modified.
63             if (entry.timestamp == iterator->second.timestamp) {
64                 iterator->second.state = FileInfo::NOCHANGE;
65             } else {
66                 iterator->second.timestamp = entry.timestamp;
67                 iterator->second.state = FileInfo::MODIFIED;
68             }
69         } else {
70             // This is a new file or directory, start tracking it.
71             files_.insert({ path, { entry.timestamp, FileInfo::ADDED } });
72         }
73         if (entry.type == IDirectory::Entry::DIRECTORY) {
74             RecursivelyCollectAllFiles(path);
75         }
76         path.resize(oldLength);
77     }
78 }
FileMonitor(IFileManager & manager)79 FileMonitor::FileMonitor(IFileManager& manager) : fileManager_(manager) {}
CleanPath(const string_view inPath,string & path)80 void FileMonitor::CleanPath(const string_view inPath, string& path)
81 {
82     // cleanup slashes. (partial sanitation, path needs to end with '/' and slashes must be '/' (not '\\')
83     if ((inPath.back() != '/') && (inPath.back() != '\\')) {
84         path.reserve(inPath.size() + 1);
85         path = inPath;
86         path += '/';
87     } else {
88         path = inPath;
89     }
90     for (auto& c : path) {
91         if (c == '\\') {
92             c = '/';
93         }
94     }
95 }
96 
AddPath(const string_view inPath)97 bool FileMonitor::AddPath(const string_view inPath)
98 {
99     string path;
100     CleanPath(inPath, path);
101     if (IsWatchingDirectory(path) || IsWatchingSubDirectory(path)) {
102         // Already exists or unable to resolve.
103         return false;
104     }
105     // Collect information for files in path.
106     RecursivelyCollectAllFiles(path);
107     if (path.capacity() > pathTmp_.capacity()) {
108         pathTmp_.reserve(path.capacity());
109     }
110     // Update state.
111     for (auto& ref : files_) {
112         ref.second.state = FileInfo::REMOVED;
113     }
114     // Store directory to watch list.
115     directories_.push_back(path);
116     return true;
117 }
118 
RemovePath(const string_view inPath)119 bool FileMonitor::RemovePath(const string_view inPath)
120 {
121     string path;
122     CleanPath(inPath, path);
123     const auto iterator = std::find(directories_.cbegin(), directories_.cend(), path);
124     if (iterator == directories_.cend()) {
125         return false;
126     }
127     // scan through tracked files, and remove the ones that start with "path"
128     for (auto it = files_.begin(); it != files_.end();) {
129         if (it->first.starts_with(path)) {
130             it = files_.erase(it);
131         } else {
132             ++it;
133         }
134     }
135     // Remove directory from watch list.
136     directories_.erase(iterator);
137     return true;
138 }
139 
IsWatchingDirectory(const string_view inPath)140 bool FileMonitor::IsWatchingDirectory(const string_view inPath)
141 {
142     string path;
143     CleanPath(inPath, path);
144     for (const auto& ref : directories_) {
145         if (path.find(ref) != string_view::npos) {
146             // Already watching this directory or it's parent.
147             return true;
148         }
149     }
150     return false;
151 }
152 
IsWatchingSubDirectory(const string_view inPath)153 bool FileMonitor::IsWatchingSubDirectory(const string_view inPath)
154 {
155     string path;
156     CleanPath(inPath, path);
157     for (const auto& ref : directories_) {
158         if (ref.find(path) != string_view::npos) {
159             // Already watching subdirectory of given directory.
160             return true;
161         }
162     }
163     return false;
164 }
165 
ScanModifications(vector<string> & added,vector<string> & removed,vector<string> & modified)166 void FileMonitor::ScanModifications(vector<string>& added, vector<string>& removed, vector<string>& modified)
167 {
168     CORE_CPU_PERF_SCOPE("CORE", "FileMonitor", "ScanModifications()", CORE_PROFILER_DEFAULT_COLOR);
169     // Collect all files that are under monitoring.
170     for (const auto& ref : directories_) {
171         pathTmp_ = ref;
172         RecursivelyCollectAllFiles(pathTmp_);
173     }
174 
175     // See which of the files are removed.
176     for (auto it = files_.begin(); it != files_.end();) {
177         if (it->second.state == FileInfo::REMOVED) {
178             removed.push_back(it->first);
179         } else if (it->second.state == FileInfo::MODIFIED) {
180             modified.push_back(it->first);
181         } else if (it->second.state == FileInfo::ADDED) {
182             added.push_back(it->first);
183         }
184         if (it->second.state != FileInfo::REMOVED) {
185             // default state is removed.
186             it->second.state = FileInfo::REMOVED;
187             ++it;
188         } else {
189             it = files_.erase(it);
190         }
191     }
192 }
193 
GetMonitoredFiles() const194 vector<string> FileMonitor::GetMonitoredFiles() const
195 {
196     vector<string> filesRes;
197     filesRes.reserve(files_.size());
198     for (auto& f : files_) {
199         filesRes.push_back(f.first);
200     }
201     return filesRes;
202 }
203 
204 CORE_END_NAMESPACE()
205