• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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 <core/io/intf_file_manager.h>
19 #include <core/namespace.h>
20 #include <core/perf/cpu_perf_scope.h>
21 #include <core/perf/intf_performance_data_manager.h>
22 
23 CORE_BEGIN_NAMESPACE()
24 using BASE_NS::string;
25 using BASE_NS::string_view;
26 using BASE_NS::vector;
27 
RecursivelyCollectAllFiles(string & path)28 void FileMonitor::RecursivelyCollectAllFiles(string& path)
29 {
30     auto dir = fileManager_.OpenDirectory(path);
31     if (dir == nullptr) {
32         // path does not exist.
33         return;
34     }
35     const auto& entries = dir->GetEntries();
36     const size_t oldLength = path.length();
37     for (const IDirectory::Entry& entry : entries) {
38         path.reserve(oldLength + entry.name.length() + 1);
39         path += entry.name;
40         if (entry.type == IDirectory::Entry::FILE) {
41             auto iterator = files_.find(path);
42             if (iterator != files_.end()) {
43                 // File being tracked, see if it is modified.
44                 if (entry.timestamp == iterator->second.timestamp) {
45                     iterator->second.state = FileInfo::NOCHANGE;
46                 } else {
47                     iterator->second.timestamp = entry.timestamp;
48                     iterator->second.state = FileInfo::MODIFIED;
49                 }
50             } else {
51                 // This is a new file, start tracking it.
52                 files_.insert({ path, { entry.timestamp, FileInfo::ADDED } });
53             }
54         } else if (entry.type == IDirectory::Entry::DIRECTORY) {
55             if (entry.name != "." && entry.name != "..") {
56                 path += '/';
57                 RecursivelyCollectAllFiles(path);
58             }
59         }
60         path.resize(oldLength);
61     }
62 }
FileMonitor(IFileManager & manager)63 FileMonitor::FileMonitor(IFileManager& manager) : fileManager_(manager) {}
CleanPath(const string_view inPath,string & path)64 void FileMonitor::CleanPath(const string_view inPath, string& path)
65 {
66     // cleanup slashes. (partial sanitation, path needs to end with '/' and slashes must be '/' (not '\\')
67     if ((inPath.back() != '/') && (inPath.back() != '\\')) {
68         path.reserve(inPath.size() + 1);
69         path = inPath;
70         path += '/';
71     } else {
72         path = inPath;
73     }
74     for (auto& c : path) {
75         if (c == '\\')
76             c = '/';
77     }
78 }
79 
AddPath(const string_view inPath)80 bool FileMonitor::AddPath(const string_view inPath)
81 {
82     string path;
83     CleanPath(inPath, path);
84     if (IsWatchingDirectory(path) || IsWatchingSubDirectory(path)) {
85         // Already exists or unable to resolve.
86         return false;
87     }
88     // Collect information for files in path.
89     RecursivelyCollectAllFiles(path);
90     if (path.capacity() > pathTmp_.capacity()) {
91         pathTmp_.reserve(path.capacity());
92     }
93     // Update state.
94     for (auto& ref : files_) {
95         ref.second.state = FileInfo::REMOVED;
96     }
97     // Store directory to watch list.
98     directories_.push_back(path);
99     return true;
100 }
101 
RemovePath(const string_view inPath)102 bool FileMonitor::RemovePath(const string_view inPath)
103 {
104     string path;
105     CleanPath(inPath, path);
106     vector<string>::iterator iterator = directories_.begin();
107     for (; iterator != directories_.end(); ++iterator) {
108         if (*iterator == path) {
109             // scan through tracked files, and remove the ones that start with "path"
110             for (auto it = files_.begin(); it != files_.end();) {
111                 const auto pos = it->first.find(path);
112                 if (pos == 0) {
113                     it = files_.erase(it);
114                 } else {
115                     ++it;
116                 }
117             }
118             // Remove directory from watch list.
119             directories_.erase(iterator);
120             return true;
121         }
122     }
123     return false;
124 }
125 
IsWatchingDirectory(const string_view inPath)126 bool FileMonitor::IsWatchingDirectory(const string_view inPath)
127 {
128     string path;
129     CleanPath(inPath, path);
130     for (const auto& ref : directories_) {
131         if (path.find(ref) != string_view::npos) {
132             // Already watching this directory or it's parent.
133             return true;
134         }
135     }
136     return false;
137 }
138 
IsWatchingSubDirectory(const string_view inPath)139 bool FileMonitor::IsWatchingSubDirectory(const string_view inPath)
140 {
141     string path;
142     CleanPath(inPath, path);
143     for (const auto& ref : directories_) {
144         if (ref.find(path) != string_view::npos) {
145             // Already watching subdirectory of given directory.
146             return true;
147         }
148     }
149     return false;
150 }
151 
ScanModifications(vector<string> & added,vector<string> & removed,vector<string> & modified)152 void FileMonitor::ScanModifications(vector<string>& added, vector<string>& removed, vector<string>& modified)
153 {
154     CORE_CPU_PERF_SCOPE("Other", "FileMonitor", "ScanModifications()");
155     // Collect all files that are under monitoring.
156     for (const auto& ref : directories_) {
157         pathTmp_ = ref;
158         RecursivelyCollectAllFiles(pathTmp_);
159     }
160 
161     // See which of the files are removed.
162     for (auto it = files_.begin(); it != files_.end();) {
163         if (it->second.state == FileInfo::REMOVED) {
164             removed.emplace_back(it->first);
165         } else if (it->second.state == FileInfo::MODIFIED) {
166             modified.emplace_back(it->first);
167         } else if (it->second.state == FileInfo::ADDED) {
168             added.emplace_back(it->first);
169         }
170         if (it->second.state != FileInfo::REMOVED) {
171             // default state is removed.
172             it->second.state = FileInfo::REMOVED;
173             ++it;
174         } else {
175             it = files_.erase(it);
176         }
177     }
178 }
179 
GetMonitoredFiles() const180 vector<string> FileMonitor::GetMonitoredFiles() const
181 {
182     vector<string> filesRes;
183     filesRes.reserve(files_.size());
184     for (auto& f : files_) {
185         filesRes.emplace_back(f.first);
186     }
187     return filesRes;
188 }
189 
190 CORE_END_NAMESPACE()
191