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