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