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