• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/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 
RegisterPath(const string_view protocol,const string_view uriIn,bool prepend)337 bool FileManager::RegisterPath(const string_view protocol, const string_view uriIn, bool prepend)
338 {
339     auto uri = FixPath(uriIn);
340     // Check if the proxy protocol exists already.
341     auto it = proxyFilesystems_.find(protocol);
342     if (it != proxyFilesystems_.end()) {
343         // Yes, add the new search path to it.
344         if (prepend) {
345             it->second->PrependSearchPath(uri);
346         } else {
347             it->second->AppendSearchPath(uri);
348         }
349         return true;
350     }
351 
352     // Check if the protocol is already declared..
353     const auto itp = filesystems_.find(protocol);
354     if (itp != filesystems_.end()) {
355         // Okay there is a protocol handler already, we can't add paths to non-proxy protocols.
356         CORE_LOG_W("Tried to register a path to non-proxy filesystem. protocol [%s] uriIn [%s]",
357             string(protocol).c_str(), string(uriIn).c_str());
358         return false;
359     }
360 
361     // Create new proxy protocol handler.
362     auto pfs = make_unique<ProxyFilesystem>(*this, uri);
363     proxyFilesystems_[protocol] = pfs.get();
364     RegisterFilesystem(protocol, IFilesystem::Ptr { pfs.release() });
365     return true;
366 }
367 
UnregisterPath(const string_view protocol,const string_view uriIn)368 void FileManager::UnregisterPath(const string_view protocol, const string_view uriIn)
369 {
370     auto uri = FixPath(uriIn);
371     auto it = proxyFilesystems_.find(protocol);
372     if (it != proxyFilesystems_.end()) {
373         it->second->RemoveSearchPath(uri);
374     }
375 }
376 
GetFilesystem(const string_view protocol) const377 IFilesystem* FileManager::GetFilesystem(const string_view protocol) const
378 {
379     const auto it = filesystems_.find(protocol);
380     if (it != filesystems_.end()) {
381         return it->second.get();
382     }
383 
384     return nullptr;
385 }
386 
CreateROFilesystem(const void * const data,uint64_t size)387 IFilesystem::Ptr FileManager::CreateROFilesystem(const void* const data, uint64_t size)
388 {
389     return IFilesystem::Ptr { new RoFileSystem(data, static_cast<size_t>(size)) };
390 }
391 CORE_END_NAMESPACE()
392