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