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