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 "file_path_utils.h" 17 18#include <fstream> 19#include <regex> 20#include <vector> 21#include "constants.h" 22#include <atomic> 23#include <chrono> 24#include <climits> 25 26#ifdef WINDOWS_PLATFORM 27#include <io.h> 28 29namespace { 30char* realpath(const char* path, char* resolvedPath) 31{ 32 if (_access(path, 0) < 0) { 33 return nullptr; 34 } 35 if (strcpy_s(resolvedPath, PATH_MAX, path) != 0) { 36 return nullptr; 37 } 38 return resolvedPath; 39} 40} 41#endif 42 43namespace panda { 44namespace ecmascript { 45namespace { 46constexpr char EXT_NAME_ABC[] = ".abc"; 47constexpr char EXT_NAME_ETS[] = ".ets"; 48constexpr char EXT_NAME_TS[] = ".ts"; 49constexpr char EXT_NAME_JS[] = ".js"; 50constexpr char PREFIX_BUNDLE[] = "@bundle:"; 51constexpr char PREFIX_MODULE[] = "@module:"; 52constexpr char PREFIX_LOCAL[] = "@local:"; 53constexpr char NPM_PATH_SEGMENT[] = "node_modules"; 54constexpr char NPM_ENTRY_FILE[] = "index.abc"; 55constexpr char NPM_ENTRY_LINK[] = "entry.txt"; 56constexpr char BUNDLE_INSTALL_PATH[] = "/data/storage/el1/bundle/"; 57constexpr char OTHER_BUNDLE_INSTALL_PATH[] = "/data/bundles/"; 58 59constexpr size_t MAX_NPM_LEVEL = 1; 60constexpr size_t SEGMENTS_LIMIT_TWO = 2; 61constexpr size_t SEGMENTS_LIMIT_THREE = 3; 62} // namespace 63 64bool StringStartWith(const std::string& str, const char* startStr, size_t startStrLen) 65{ 66 return ((str.length() >= startStrLen) && (str.compare(0, startStrLen, startStr) == 0)); 67} 68 69bool StringEndWith(const std::string& str, const char* endStr, size_t endStrLen) 70{ 71 size_t len = str.length(); 72 return ((len >= endStrLen) && (str.compare(len - endStrLen, endStrLen, endStr) == 0)); 73} 74 75void SplitString(const std::string& str, std::vector<std::string>& out, size_t pos, const char* seps) 76{ 77 if (str.empty() || pos >= str.length()) { 78 return; 79 } 80 81 size_t startPos = pos; 82 size_t endPos = 0; 83 while ((endPos = str.find_first_of(seps, startPos)) != std::string::npos) { 84 if (endPos > startPos) { 85 out.emplace_back(str.substr(startPos, endPos - startPos)); 86 } 87 startPos = endPos + 1; 88 } 89 90 if (startPos < str.length()) { 91 out.emplace_back(str.substr(startPos)); 92 } 93} 94 95std::string JoinString(const std::vector<std::string>& strs, char sep, size_t startIndex) 96{ 97 std::string out; 98 for (size_t index = startIndex; index < strs.size(); ++index) { 99 if (!strs[index].empty()) { 100 out.append(strs[index]) += sep; 101 } 102 } 103 if (!out.empty()) { 104 out.pop_back(); 105 } 106 return out; 107} 108 109std::string StripString(const std::string& str, const char* charSet) 110{ 111 size_t startPos = str.find_first_not_of(charSet); 112 if (startPos == std::string::npos) { 113 return std::string(); 114 } 115 116 return str.substr(startPos, str.find_last_not_of(charSet) - startPos + 1); 117} 118 119void FixExtName(std::string& path) 120{ 121 if (path.empty()) { 122 return; 123 } 124 125 if (StringEndWith(path, EXT_NAME_ABC, sizeof(EXT_NAME_ABC) - 1)) { 126 return; 127 } 128 129 if (StringEndWith(path, EXT_NAME_ETS, sizeof(EXT_NAME_ETS) - 1)) { 130 path.erase(path.length() - (sizeof(EXT_NAME_ETS) - 1), sizeof(EXT_NAME_ETS) - 1); 131 } else if (StringEndWith(path, EXT_NAME_TS, sizeof(EXT_NAME_TS) - 1)) { 132 path.erase(path.length() - (sizeof(EXT_NAME_TS) - 1), sizeof(EXT_NAME_TS) - 1); 133 } else if (StringEndWith(path, EXT_NAME_JS, sizeof(EXT_NAME_JS) - 1)) { 134 path.erase(path.length() - (sizeof(EXT_NAME_JS) - 1), sizeof(EXT_NAME_JS) - 1); 135 } 136 137 path.append(EXT_NAME_ABC); 138} 139 140std::string GetInstallPath(const std::string& curJsModulePath, bool module) 141{ 142 size_t pos = std::string::npos; 143 if (StringStartWith(curJsModulePath, BUNDLE_INSTALL_PATH, std::string(BUNDLE_INSTALL_PATH).length())) { 144 pos = std::string(BUNDLE_INSTALL_PATH).length() - 1; 145 } else { 146 if (!StringStartWith(curJsModulePath, OTHER_BUNDLE_INSTALL_PATH, 147 std::string(OTHER_BUNDLE_INSTALL_PATH).length())) { 148 return std::string(); 149 } 150 151 pos = curJsModulePath.find('/', std::string(OTHER_BUNDLE_INSTALL_PATH).length()); 152 if (pos == std::string::npos) { 153 return std::string(); 154 } 155 } 156 157 if (module) { 158 pos = curJsModulePath.find('/', pos + 1); 159 if (pos == std::string::npos) { 160 return std::string(); 161 } 162 } 163 164 return curJsModulePath.substr(0, pos + 1); 165} 166 167std::string MakeNewJsModulePath( 168 const std::string& curJsModulePath, const std::string& newJsModuleUri) 169{ 170 std::string moduleInstallPath = GetInstallPath(curJsModulePath, true); 171 if (moduleInstallPath.empty()) { 172 return std::string(); 173 } 174 175 std::vector<std::string> pathVector; 176 SplitString(curJsModulePath, pathVector, moduleInstallPath.length()); 177 178 if (pathVector.empty()) { 179 return std::string(); 180 } 181 182 // Remove file name, reserve only dir name 183 pathVector.pop_back(); 184 185 std::vector<std::string> relativePathVector; 186 SplitString(newJsModuleUri, relativePathVector); 187 188 for (auto& value : relativePathVector) { 189 if (value == ".") { 190 continue; 191 } else if (value == "..") { 192 if (pathVector.empty()) { 193 return std::string(); 194 } 195 pathVector.pop_back(); 196 } else { 197 pathVector.emplace_back(std::move(value)); 198 } 199 } 200 201 std::string jsModulePath = moduleInstallPath + JoinString(pathVector, '/'); 202 FixExtName(jsModulePath); 203 if (jsModulePath.size() >= PATH_MAX) { 204 return std::string(); 205 } 206 207 char path[PATH_MAX]; 208 if (realpath(jsModulePath.c_str(), path) != nullptr) { 209 return std::string(path); 210 } 211 return std::string(); 212} 213 214std::string FindNpmPackageInPath(const std::string& npmPath) 215{ 216 std::string fileName = npmPath + "/" + NPM_ENTRY_FILE; 217 218 char path[PATH_MAX]; 219 if (fileName.size() >= PATH_MAX) { 220 return std::string(); 221 } 222 if (realpath(fileName.c_str(), path) != nullptr) { 223 return path; 224 } 225 226 fileName = npmPath + "/" + NPM_ENTRY_LINK; 227 if (fileName.size() >= PATH_MAX) { 228 return std::string(); 229 } 230 if (realpath(fileName.c_str(), path) == nullptr) { 231 return std::string(); 232 } 233 234 std::ifstream stream(path, std::ios::ate); 235 if (!stream.is_open()) { 236 return std::string(); 237 } 238 239 auto fileLen = stream.tellg(); 240 if (fileLen >= PATH_MAX) { 241 return std::string(); 242 } 243 244 stream.seekg(0); 245 stream.read(path, fileLen); 246 path[fileLen] = '\0'; 247 stream.close(); 248 249 std::string npmPackagePath = npmPath + '/' + StripString(path); 250 if (npmPackagePath.size() >= PATH_MAX) { 251 return std::string(); 252 } 253 if (realpath(npmPackagePath.c_str(), path) == nullptr) { 254 return std::string(); 255 } 256 return path; 257} 258 259std::string FindNpmPackageInTopLevel( 260 const std::string& moduleInstallPath, const std::string& npmPackage, size_t start) 261{ 262 for (size_t level = start; level <= MAX_NPM_LEVEL; ++level) { 263 std::string path = moduleInstallPath + NPM_PATH_SEGMENT + '/' + std::to_string(level) + '/' + npmPackage; 264 path = FindNpmPackageInPath(path); 265 if (!path.empty()) { 266 return path; 267 } 268 } 269 270 return std::string(); 271} 272 273std::string FindNpmPackage(const std::string& curJsModulePath, const std::string& npmPackage) 274{ 275 std::string newJsModulePath = MakeNewJsModulePath(curJsModulePath, npmPackage); 276 if (!newJsModulePath.empty()) { 277 return newJsModulePath; 278 } 279 std::string moduleInstallPath = GetInstallPath(curJsModulePath); 280 if (moduleInstallPath.empty()) { 281 return std::string(); 282 } 283 std::vector<std::string> pathVector; 284 SplitString(curJsModulePath, pathVector, moduleInstallPath.length()); 285 if (pathVector.empty()) { 286 return std::string(); 287 } 288 289 if (pathVector[0] != NPM_PATH_SEGMENT) { 290 return FindNpmPackageInTopLevel(moduleInstallPath, npmPackage); 291 } 292 293 // Remove file name, reserve only dir name 294 pathVector.pop_back(); 295 296 // Find npm package until reach top level npm path such as 'node_modules/0', 297 // so there must be 2 element in vector 298 while (pathVector.size() > 2) { 299 std::string path = 300 moduleInstallPath + JoinString(pathVector, '/') + '/' + NPM_PATH_SEGMENT + '/' + npmPackage; 301 path = FindNpmPackageInPath(path); 302 if (!path.empty()) { 303 return path; 304 } 305 306 pathVector.pop_back(); 307 } 308 309 char* p = nullptr; 310 size_t index = std::strtoul(pathVector.back().c_str(), &p, 10); 311 if (p == nullptr || *p != '\0') { 312 return std::string(); 313 } 314 315 return FindNpmPackageInTopLevel(moduleInstallPath, npmPackage, index); 316} 317 318std::string ParseOhmUri( 319 const std::string& originBundleName, const std::string& curJsModulePath, const std::string& newJsModuleUri) 320{ 321 std::string moduleInstallPath; 322 std::vector<std::string> pathVector; 323 size_t index = 0; 324 325 if (StringStartWith(newJsModuleUri, PREFIX_BUNDLE, sizeof(PREFIX_BUNDLE) - 1)) { 326 SplitString(newJsModuleUri, pathVector, sizeof(PREFIX_BUNDLE) - 1); 327 328 // Uri should have atleast 3 segments 329 if (pathVector.size() < SEGMENTS_LIMIT_THREE) { 330 return std::string(); 331 } 332 333 const auto& bundleName = pathVector[index++]; 334 if (bundleName == originBundleName) { 335 moduleInstallPath = std::string(BUNDLE_INSTALL_PATH); 336 } else { 337 moduleInstallPath = std::string(OTHER_BUNDLE_INSTALL_PATH); 338 moduleInstallPath.append(bundleName).append("/"); 339 } 340 moduleInstallPath.append(pathVector[index++]).append("/"); 341 } else if (StringStartWith(newJsModuleUri, PREFIX_MODULE, sizeof(PREFIX_MODULE) - 1)) { 342 SplitString(newJsModuleUri, pathVector, sizeof(PREFIX_MODULE) - 1); 343 344 // Uri should have atleast 2 segments 345 if (pathVector.size() < SEGMENTS_LIMIT_TWO) { 346 return std::string(); 347 } 348 349 moduleInstallPath = GetInstallPath(curJsModulePath, false); 350 if (moduleInstallPath.empty()) { 351 return std::string(); 352 } 353 moduleInstallPath.append(pathVector[index++]).append("/"); 354 } else if (StringStartWith(newJsModuleUri, PREFIX_LOCAL, sizeof(PREFIX_LOCAL) - 1)) { 355 SplitString(newJsModuleUri, pathVector, sizeof(PREFIX_LOCAL) - 1); 356 357 if (pathVector.empty()) { 358 return std::string(); 359 } 360 361 moduleInstallPath = GetInstallPath(curJsModulePath); 362 if (moduleInstallPath.empty()) { 363 return std::string(); 364 } 365 } else { 366 return std::string(); 367 } 368 369 if (pathVector[index] != NPM_PATH_SEGMENT) { 370 return moduleInstallPath + JoinString(pathVector, '/', index); 371 } 372 373 return FindNpmPackageInTopLevel(moduleInstallPath, JoinString(pathVector, '/', index + 1)); 374} 375 376std::string NormalizeUri( 377 const std::string& bundleName, const std::string& curJsModulePath, const std::string& newJsModuleUri) 378{ 379 std::string newJsModulePath; 380 if (curJsModulePath.empty() || newJsModuleUri.empty()) { 381 return newJsModulePath; 382 } 383 384 std::string normalizeUri = newJsModuleUri; 385 std::replace(normalizeUri.begin(), normalizeUri.end(), '\\', '/'); 386 387 switch (normalizeUri[0]) { 388 case '.': { 389 newJsModulePath = MakeNewJsModulePath(curJsModulePath, normalizeUri); 390 break; 391 } 392 case '@': { 393 newJsModulePath = ParseOhmUri(bundleName, curJsModulePath, normalizeUri); 394 if (newJsModulePath.empty()) { 395 newJsModulePath = FindNpmPackage(curJsModulePath, normalizeUri); 396 } 397 break; 398 } 399 default: { 400 newJsModulePath = FindNpmPackage(curJsModulePath, normalizeUri); 401 break; 402 } 403 } 404 405 FixExtName(newJsModulePath); 406 return newJsModulePath; 407} 408 409bool MakeFilePath(const std::string& codePath, const std::string& modulePath, std::string& fileName) 410{ 411 std::string path(codePath); 412 path.append("/").append(modulePath); 413 if (path.length() > PATH_MAX) { 414 return false; 415 } 416 char resolvedPath[PATH_MAX + 1] = { 0 }; 417 if (realpath(path.c_str(), resolvedPath) != nullptr) { 418 fileName = resolvedPath; 419 return true; 420 } 421 422 auto start = path.find_last_of('/'); 423 auto end = path.find_last_of('.'); 424 if (end == std::string::npos || end == 0) { 425 return false; 426 } 427 428 auto pos = path.find_last_of('.', end - 1); 429 if (pos == std::string::npos) { 430 return false; 431 } 432 433 path.erase(start + 1, pos - start); 434 435 if (realpath(path.c_str(), resolvedPath) == nullptr) { 436 return false; 437 } 438 439 fileName = resolvedPath; 440 return true; 441} 442 443std::string GetLoadPath(const std::string& hapPath) 444{ 445 std::regex hapPattern(std::string(Constants::ABS_CODE_PATH) + std::string(Constants::FILE_SEPARATOR)); 446 std::string loadPath = std::regex_replace(hapPath, hapPattern, ""); 447 loadPath = std::string(Constants::LOCAL_CODE_PATH) + std::string(Constants::FILE_SEPARATOR) + 448 loadPath.substr(loadPath.find(std::string(Constants::FILE_SEPARATOR)) + 1); 449 return loadPath; 450} 451 452std::string GetRelativePath(const std::string& srcPath) 453{ 454 if (srcPath.empty() || srcPath[0] != '/') { 455 return srcPath; 456 } 457 std::regex srcPattern(Constants::LOCAL_CODE_PATH); 458 std::string relativePath = std::regex_replace(srcPath, srcPattern, ""); 459 if (relativePath.find(Constants::FILE_SEPARATOR) == 0) { 460 relativePath = relativePath.substr(1); 461 relativePath = relativePath.substr(relativePath.find(std::string(Constants::FILE_SEPARATOR)) + 1); 462 } 463 return relativePath; 464} 465} // namespace AbilityBase 466} // namespace OHOS 467