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