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/file_manager.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 <base/util/uid.h>
30 #include <core/io/intf_directory.h>
31 #include <core/io/intf_file.h>
32 #include <core/io/intf_file_manager.h>
33 #include <core/io/intf_file_system.h>
34 #include <core/log.h>
35 #include <core/namespace.h>
36 #include <core/plugin/intf_interface.h>
37
38 #include "io/path_tools.h"
39 #include "io/proxy_filesystem.h"
40 #include "io/rofs_filesystem.h"
41 #include "io/std_directory.h"
42
43 CORE_BEGIN_NAMESPACE()
44 using BASE_NS::make_unique;
45 using BASE_NS::string;
46 using BASE_NS::string_view;
47 using BASE_NS::Uid;
48 using BASE_NS::vector;
49
FixPath(string_view pathIn) const50 string FileManager::FixPath(string_view pathIn) const
51 {
52 string_view protocol;
53 string_view path;
54 if (ParseUri(pathIn, protocol, path)) {
55 // Try to identify relative "file" uris, and convert them to absolute.
56 if (protocol == "file") {
57 if (path.empty()) {
58 // so it's the base path then? (empty relative path)
59 return protocol + "://" + basePath_;
60 }
61 #if _WIN32
62 // Handle win32 specific drive letters.
63 if (IsRelative(path)) {
64 // might still be absolute (if it has drive letter)
65 if ((path.size() > 1) && (path[1] == ':')) {
66 // seems to start with drive letter so, it must be absolute?
67 if (path.size() == 2) { // 2: path size
68 // has only drive letter? consider it as root of drive then
69 return protocol + ":///" + path + "/";
70 }
71 return protocol + ":///" + path;
72 }
73 // no drive letter so it's really relative.
74 return protocol + "://" + NormalizePath(basePath_ + path);
75 }
76 // Even if it's "absolute" it might still be missing the drive letter.
77 if ((path.size() < 3) || (path[2] != ':')) { // 3: path size limit; 2: the third letter
78 // seems to be missing the drive letter.
79 return protocol + "://" + NormalizePath(basePath_.substr(0, 3) + path); // 3: substring size
80 }
81 if (path.size() == 3) { // 3: path size
82 // has only drive letter? consider it as root of drive then
83 return protocol + "://" + path + "/";
84 }
85 return protocol + "://" + NormalizePath(path);
86 #else
87 if (IsRelative(path)) {
88 // normalize it with current path..
89 return protocol + "://" + NormalizePath(basePath_ + path);
90 }
91 return protocol + "://" + NormalizePath(path);
92 #endif
93 }
94 }
95 return string(pathIn);
96 }
97
FileManager()98 FileManager::FileManager() : basePath_(GetCurrentDirectory()) {}
99
GetInterface(const Uid & uid) const100 const IInterface* FileManager::GetInterface(const Uid& uid) const
101 {
102 return const_cast<FileManager*>(this)->GetInterface(uid);
103 }
104
GetInterface(const Uid & uid)105 IInterface* FileManager::GetInterface(const Uid& uid)
106 {
107 if ((uid == IInterface::UID) || (uid == IFileManager::UID)) {
108 return this;
109 }
110 return nullptr;
111 }
112
Ref()113 void FileManager::Ref()
114 {
115 refCount_++;
116 }
117
Unref()118 void FileManager::Unref()
119 {
120 if (--refCount_ == 0) {
121 delete this;
122 }
123 }
124
OpenFile(const string_view uriIn)125 IFile::Ptr FileManager::OpenFile(const string_view uriIn)
126 {
127 string_view protocol;
128 string_view path;
129 auto uri = FixPath(uriIn);
130 if (ParseUri(uri, protocol, path)) {
131 IFilesystem* filesystem = GetFilesystem(protocol);
132 if (filesystem) {
133 return filesystem->OpenFile(path);
134 }
135 CORE_LOG_E("Failed to open file, no file system for uri: '%s'", string(uri).c_str());
136 } else {
137 CORE_LOG_E("Failed to open file, invalid uri: '%s'", string(uri).c_str());
138 }
139
140 return IFile::Ptr();
141 }
142
CreateFile(const string_view uriIn)143 IFile::Ptr FileManager::CreateFile(const string_view uriIn)
144 {
145 string_view protocol;
146 string_view path;
147 auto uri = FixPath(uriIn);
148 if (ParseUri(uri, protocol, path)) {
149 IFilesystem* filesystem = GetFilesystem(protocol);
150 if (filesystem) {
151 return filesystem->CreateFile(path);
152 }
153 CORE_LOG_E("Failed to create file, no file system for uri: '%s'", string(uri).c_str());
154 } else {
155 CORE_LOG_E("Failed to create file, invalid uri: '%s'", string(uri).c_str());
156 }
157
158 return IFile::Ptr();
159 }
160
DeleteFile(const string_view uriIn)161 bool FileManager::DeleteFile(const string_view uriIn)
162 {
163 string_view protocol;
164 string_view path;
165 auto uri = FixPath(uriIn);
166 if (ParseUri(uri, protocol, path)) {
167 IFilesystem* filesystem = GetFilesystem(protocol);
168 if (filesystem) {
169 return filesystem->DeleteFile(path);
170 }
171 }
172
173 return false;
174 }
175
Rename(const string_view fromUri,const string_view toUri)176 bool FileManager::Rename(const string_view fromUri, const string_view toUri)
177 {
178 string_view fromProtocol;
179 string_view fromPath;
180 auto from = FixPath(fromUri);
181 if (ParseUri(from, fromProtocol, fromPath)) {
182 string_view toProtocol;
183 string_view toPath;
184 auto to = FixPath(toUri);
185 if (ParseUri(to, toProtocol, toPath)) {
186 if (fromProtocol == toProtocol) {
187 IFilesystem* filesystem = GetFilesystem(fromProtocol);
188 if (filesystem) {
189 return filesystem->Rename(fromPath, toPath);
190 }
191 } else {
192 CORE_LOG_E("Rename requires both uris have same protocol");
193 }
194 }
195 }
196
197 return false;
198 }
199
GetEntry(const string_view uriIn)200 IDirectory::Entry FileManager::GetEntry(const string_view uriIn)
201 {
202 string_view protocol;
203 string_view path;
204 auto uri = FixPath(uriIn);
205 if (ParseUri(uri, protocol, path)) {
206 IFilesystem* filesystem = GetFilesystem(protocol);
207 if (filesystem) {
208 return filesystem->GetEntry(path);
209 }
210 CORE_LOG_E("Failed to get entry for uri, no file system for uri: '%s'", string(uri).c_str());
211 } else {
212 CORE_LOG_E("Failed to get entry for uri, invalid uri: '%s'", string(uri).c_str());
213 }
214
215 return {};
216 }
OpenDirectory(const string_view uriIn)217 IDirectory::Ptr FileManager::OpenDirectory(const string_view uriIn)
218 {
219 string_view protocol;
220 string_view path;
221 auto uri = FixPath(uriIn);
222 if (ParseUri(uri, protocol, path)) {
223 IFilesystem* filesystem = GetFilesystem(protocol);
224 if (filesystem) {
225 return filesystem->OpenDirectory(path);
226 }
227 CORE_LOG_E("Failed to open directory, no file system for uri: '%s'", string(uri).c_str());
228 } else {
229 CORE_LOG_E("Failed to open directory, invalid uri: '%s'", string(uri).c_str());
230 }
231
232 return IDirectory::Ptr();
233 }
234
CreateDirectory(const string_view uriIn)235 IDirectory::Ptr FileManager::CreateDirectory(const string_view uriIn)
236 {
237 string_view protocol;
238 string_view path;
239 auto uri = FixPath(uriIn);
240 if (ParseUri(uri, protocol, path)) {
241 IFilesystem* filesystem = GetFilesystem(protocol);
242 if (filesystem) {
243 return filesystem->CreateDirectory(path);
244 }
245 CORE_LOG_E("Failed to create directory, no file system for uri: '%s'", string(uri).c_str());
246 } else {
247 CORE_LOG_E("Failed to create directory, invalid uri: '%s'", string(uri).c_str());
248 }
249
250 return IDirectory::Ptr();
251 }
252
DeleteDirectory(const string_view uriIn)253 bool FileManager::DeleteDirectory(const string_view uriIn)
254 {
255 string_view protocol;
256 string_view path;
257 auto uri = FixPath(uriIn);
258 if (ParseUri(uri, protocol, path)) {
259 IFilesystem* filesystem = GetFilesystem(protocol);
260 if (filesystem) {
261 return filesystem->DeleteDirectory(path);
262 }
263 }
264
265 return false;
266 }
267
RegisterFilesystem(const string_view protocol,IFilesystem::Ptr filesystem)268 void FileManager::RegisterFilesystem(const string_view protocol, IFilesystem::Ptr filesystem)
269 {
270 CORE_ASSERT_MSG(filesystems_.find(protocol) == filesystems_.cend(), "File system already registered");
271
272 filesystems_[protocol] = move(filesystem);
273 }
274
UnregisterFilesystem(const string_view protocol)275 void FileManager::UnregisterFilesystem(const string_view protocol)
276 {
277 const auto iterator = filesystems_.find(protocol);
278 if (iterator != filesystems_.end()) {
279 filesystems_.erase(iterator);
280 }
281 }
282
RegisterAssetPath(const string_view uriIn)283 void FileManager::RegisterAssetPath(const string_view uriIn)
284 {
285 auto uri = FixPath(uriIn);
286 RegisterPath("assets", uri, false);
287 }
288
UnregisterAssetPath(const string_view uriIn)289 void FileManager::UnregisterAssetPath(const string_view uriIn)
290 {
291 auto uri = FixPath(uriIn);
292 UnregisterPath("assets", uri);
293 }
294
GetAbsolutePaths(const string_view uriIn) const295 vector<string> FileManager::GetAbsolutePaths(const string_view uriIn) const
296 {
297 vector<string> ret;
298 string_view protocol;
299 string_view path;
300 auto uri = FixPath(uriIn);
301 if (ParseUri(uri, protocol, path)) {
302 const IFilesystem* filesystem = GetFilesystem(protocol);
303 if (filesystem) {
304 // a single URI path can be found in several paths in a proxy filesystem
305 auto uriPaths = filesystem->GetUriPaths(path);
306 for (auto& uriPath : uriPaths) {
307 if (uriPath.find("file://") == string::npos) {
308 auto tmp = GetAbsolutePaths(uriPath);
309 ret.insert(ret.cend(), tmp.cbegin(), tmp.cend());
310 } else {
311 ret.push_back(move(uriPath));
312 }
313 }
314 }
315 }
316 std::transform(ret.begin(), ret.end(), ret.begin(), [](const string& uri) {
317 string_view protocol;
318 string_view path;
319 if (ParseUri(uri, protocol, path)) {
320 return StdDirectory::ResolveAbsolutePath(path, true);
321 }
322 return uri;
323 });
324
325 return ret;
326 }
327
RegisterPath(const string_view protocol,const string_view uriIn,bool prepend)328 bool FileManager::RegisterPath(const string_view protocol, const string_view uriIn, bool prepend)
329 {
330 auto uri = FixPath(uriIn);
331 // Check if the proxy protocol exists already.
332 auto it = proxyFilesystems_.find(protocol);
333 if (it != proxyFilesystems_.end()) {
334 // Yes, add the new search path to it.
335 if (prepend) {
336 it->second->PrependSearchPath(uri);
337 } else {
338 it->second->AppendSearchPath(uri);
339 }
340 return true;
341 }
342
343 // Check if the protocol is already declared..
344 const auto itp = filesystems_.find(protocol);
345 if (itp != filesystems_.end()) {
346 // Okay there is a protocol handler already, we can't add paths to non-proxy protocols.
347 CORE_LOG_W("Tried to register a path to non-proxy filesystem. protocol [%s] uriIn [%s]",
348 string(protocol).c_str(), string(uriIn).c_str());
349 return false;
350 }
351
352 // Create new proxy protocol handler.
353 auto pfs = make_unique<ProxyFilesystem>(*this, uri);
354 proxyFilesystems_[protocol] = pfs.get();
355 RegisterFilesystem(protocol, IFilesystem::Ptr { pfs.release() });
356 return true;
357 }
358
UnregisterPath(const string_view protocol,const string_view uriIn)359 void FileManager::UnregisterPath(const string_view protocol, const string_view uriIn)
360 {
361 auto uri = FixPath(uriIn);
362 auto it = proxyFilesystems_.find(protocol);
363 if (it != proxyFilesystems_.end()) {
364 it->second->RemoveSearchPath(uri);
365 }
366 }
367
GetFilesystem(const string_view protocol) const368 IFilesystem* FileManager::GetFilesystem(const string_view protocol) const
369 {
370 const auto it = filesystems_.find(protocol);
371 if (it != filesystems_.end()) {
372 return it->second.get();
373 }
374
375 return nullptr;
376 }
377
CreateROFilesystem(const void * const data,uint64_t size)378 IFilesystem::Ptr FileManager::CreateROFilesystem(const void* const data, uint64_t size)
379 {
380 return IFilesystem::Ptr { new RoFileSystem(data, static_cast<size_t>(size)) };
381 }
382 CORE_END_NAMESPACE()
383