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