1 /*
2 * Copyright (c) 2022 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
22 #include "constants.h"
23
24 #ifdef WINDOWS_PLATFORM
25 #include <io.h>
26
27 namespace {
realpath(const char * path,char * resolvedPath)28 char* realpath(const char* path, char* resolvedPath)
29 {
30 if (_access(path, 0) < 0) {
31 return nullptr;
32 }
33 if (strcpy_s(resolvedPath, PATH_MAX, path) != 0) {
34 return nullptr;
35 }
36 return resolvedPath;
37 }
38 }
39 #endif
40
41 namespace OHOS {
42 namespace AbilityBase {
43 namespace {
44 constexpr char EXT_NAME_ABC[] = ".abc";
45 constexpr char EXT_NAME_ETS[] = ".ets";
46 constexpr char EXT_NAME_TS[] = ".ts";
47 constexpr char EXT_NAME_JS[] = ".js";
48 constexpr char PREFIX_BUNDLE[] = "@bundle:";
49 constexpr char PREFIX_MODULE[] = "@module:";
50 constexpr char PREFIX_LOCAL[] = "@local:";
51 constexpr char NPM_PATH_SEGMENT[] = "node_modules";
52 constexpr char NPM_ENTRY_FILE[] = "index.abc";
53 constexpr char NPM_ENTRY_LINK[] = "entry.txt";
54 constexpr char BUNDLE_INSTALL_PATH[] = "/data/storage/el1/bundle/";
55 constexpr char OTHER_BUNDLE_INSTALL_PATH[] = "/data/bundles/";
56
57 constexpr size_t MAX_NPM_LEVEL = 1;
58 constexpr size_t SEGMENTS_LIMIT_TWO = 2;
59 constexpr size_t SEGMENTS_LIMIT_THREE = 3;
60 } // namespace
61
StringStartWith(const std::string & str,const char * startStr,size_t startStrLen)62 bool StringStartWith(const std::string& str, const char* startStr, size_t startStrLen)
63 {
64 return ((str.length() >= startStrLen) && (str.compare(0, startStrLen, startStr) == 0));
65 }
66
StringEndWith(const std::string & str,const char * endStr,size_t endStrLen)67 bool StringEndWith(const std::string& str, const char* endStr, size_t endStrLen)
68 {
69 size_t len = str.length();
70 return ((len >= endStrLen) && (str.compare(len - endStrLen, endStrLen, endStr) == 0));
71 }
72
SplitString(const std::string & str,std::vector<std::string> & out,size_t pos,const char * seps)73 void SplitString(const std::string& str, std::vector<std::string>& out, size_t pos, const char* seps)
74 {
75 if (str.empty() || pos >= str.length()) {
76 return;
77 }
78
79 size_t startPos = pos;
80 size_t endPos = 0;
81 while ((endPos = str.find_first_of(seps, startPos)) != std::string::npos) {
82 if (endPos > startPos) {
83 out.emplace_back(str.substr(startPos, endPos - startPos));
84 }
85 startPos = endPos + 1;
86 }
87
88 if (startPos < str.length()) {
89 out.emplace_back(str.substr(startPos));
90 }
91 }
92
JoinString(const std::vector<std::string> & strs,char sep,size_t startIndex)93 std::string JoinString(const std::vector<std::string>& strs, char sep, size_t startIndex)
94 {
95 std::string out;
96 for (size_t index = startIndex; index < strs.size(); ++index) {
97 if (!strs[index].empty()) {
98 out.append(strs[index]) += sep;
99 }
100 }
101 if (!out.empty()) {
102 out.pop_back();
103 }
104 return out;
105 }
106
StripString(const std::string & str,const char * charSet)107 std::string StripString(const std::string& str, const char* charSet)
108 {
109 size_t startPos = str.find_first_not_of(charSet);
110 if (startPos == std::string::npos) {
111 return std::string();
112 }
113
114 return str.substr(startPos, str.find_last_not_of(charSet) - startPos + 1);
115 }
116
FixExtName(std::string & path)117 void FixExtName(std::string& path)
118 {
119 if (path.empty()) {
120 return;
121 }
122
123 if (StringEndWith(path, EXT_NAME_ABC, sizeof(EXT_NAME_ABC) - 1)) {
124 return;
125 }
126
127 if (StringEndWith(path, EXT_NAME_ETS, sizeof(EXT_NAME_ETS) - 1)) {
128 path.erase(path.length() - (sizeof(EXT_NAME_ETS) - 1), sizeof(EXT_NAME_ETS) - 1);
129 } else if (StringEndWith(path, EXT_NAME_TS, sizeof(EXT_NAME_TS) - 1)) {
130 path.erase(path.length() - (sizeof(EXT_NAME_TS) - 1), sizeof(EXT_NAME_TS) - 1);
131 } else if (StringEndWith(path, EXT_NAME_JS, sizeof(EXT_NAME_JS) - 1)) {
132 path.erase(path.length() - (sizeof(EXT_NAME_JS) - 1), sizeof(EXT_NAME_JS) - 1);
133 }
134
135 path.append(EXT_NAME_ABC);
136 }
137
GetInstallPath(const std::string & curJsModulePath,bool module)138 std::string GetInstallPath(const std::string& curJsModulePath, bool module)
139 {
140 size_t pos = std::string::npos;
141 if (StringStartWith(curJsModulePath, BUNDLE_INSTALL_PATH, std::string(BUNDLE_INSTALL_PATH).length())) {
142 pos = std::string(BUNDLE_INSTALL_PATH).length() - 1;
143 } else {
144 if (!StringStartWith(curJsModulePath, OTHER_BUNDLE_INSTALL_PATH,
145 std::string(OTHER_BUNDLE_INSTALL_PATH).length())) {
146 return std::string();
147 }
148
149 pos = curJsModulePath.find('/', std::string(OTHER_BUNDLE_INSTALL_PATH).length());
150 if (pos == std::string::npos) {
151 return std::string();
152 }
153 }
154
155 if (module) {
156 pos = curJsModulePath.find('/', pos + 1);
157 if (pos == std::string::npos) {
158 return std::string();
159 }
160 }
161
162 return curJsModulePath.substr(0, pos + 1);
163 }
164
MakeNewJsModulePath(const std::string & curJsModulePath,const std::string & newJsModuleUri)165 std::string MakeNewJsModulePath(
166 const std::string& curJsModulePath, const std::string& newJsModuleUri)
167 {
168 std::string moduleInstallPath = GetInstallPath(curJsModulePath, true);
169 if (moduleInstallPath.empty()) {
170 return std::string();
171 }
172
173 std::vector<std::string> pathVector;
174 SplitString(curJsModulePath, pathVector, moduleInstallPath.length());
175
176 if (pathVector.empty()) {
177 return std::string();
178 }
179
180 // Remove file name, reserve only dir name
181 pathVector.pop_back();
182
183 std::vector<std::string> relativePathVector;
184 SplitString(newJsModuleUri, relativePathVector);
185
186 for (auto& value : relativePathVector) {
187 if (value == ".") {
188 continue;
189 } else if (value == "..") {
190 if (pathVector.empty()) {
191 return std::string();
192 }
193 pathVector.pop_back();
194 } else {
195 pathVector.emplace_back(std::move(value));
196 }
197 }
198
199 std::string jsModulePath = moduleInstallPath + JoinString(pathVector, '/');
200 FixExtName(jsModulePath);
201 if (jsModulePath.size() >= PATH_MAX) {
202 return std::string();
203 }
204
205 char path[PATH_MAX];
206 if (realpath(jsModulePath.c_str(), path) != nullptr) {
207 return std::string(path);
208 }
209 return std::string();
210 }
211
FindNpmPackageInPath(const std::string & npmPath)212 std::string FindNpmPackageInPath(const std::string& npmPath)
213 {
214 std::string fileName = npmPath + "/" + NPM_ENTRY_FILE;
215
216 char path[PATH_MAX];
217 if (fileName.size() >= PATH_MAX) {
218 return std::string();
219 }
220 if (realpath(fileName.c_str(), path) != nullptr) {
221 return path;
222 }
223
224 fileName = npmPath + "/" + NPM_ENTRY_LINK;
225 if (fileName.size() >= PATH_MAX) {
226 return std::string();
227 }
228 if (realpath(fileName.c_str(), path) == nullptr) {
229 return std::string();
230 }
231
232 std::ifstream stream(path, std::ios::ate);
233 if (!stream.is_open()) {
234 return std::string();
235 }
236
237 auto fileLength = stream.tellg();
238 if (fileLength >= PATH_MAX) {
239 stream.close();
240 return std::string();
241 }
242
243 stream.seekg(0);
244 stream.read(path, fileLength);
245 path[fileLength] = '\0';
246 stream.close();
247
248 std::string npmPackagePathString = npmPath + '/' + StripString(path);
249 if (npmPackagePathString.size() >= PATH_MAX) {
250 return std::string();
251 }
252 if (realpath(npmPackagePathString.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 startLevel = start; startLevel <= MAX_NPM_LEVEL; ++startLevel) {
262 std::string pathString = moduleInstallPath + NPM_PATH_SEGMENT + '/' +
263 std::to_string(startLevel) + '/' + npmPackage;
264 pathString = FindNpmPackageInPath(pathString);
265 if (!pathString.empty()) {
266 return pathString;
267 }
268 }
269
270 return std::string();
271 }
272
FindNpmPackage(const std::string & curJsModulePath,const std::string & npmPackageString)273 std::string FindNpmPackage(const std::string& curJsModulePath, const std::string& npmPackageString)
274 {
275 std::string newJsModulePathString = MakeNewJsModulePath(curJsModulePath, npmPackageString);
276 if (!newJsModulePathString.empty()) {
277 return newJsModulePathString;
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, npmPackageString);
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 + '/' + npmPackageString;
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 indexNum = std::strtoul(pathVector.back().c_str(), &p, 10);
311 if (p == nullptr || *p != '\0') {
312 return std::string();
313 }
314
315 return FindNpmPackageInTopLevel(moduleInstallPath, npmPackageString, indexNum);
316 }
317
ParseOhmUri(const std::string & originBundleName,const std::string & curJsModulePath,const std::string & newJsModuleUri)318 std::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 indexNumber = 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[indexNumber++];
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[indexNumber++]).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[indexNumber++]).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[indexNumber] != NPM_PATH_SEGMENT) {
370 return moduleInstallPath + JoinString(pathVector, '/', indexNumber);
371 }
372
373 return FindNpmPackageInTopLevel(moduleInstallPath, JoinString(pathVector, '/', indexNumber + 1));
374 }
375
NormalizeUri(const std::string & bundleName,const std::string & curJsModulePath,const std::string & newJsModuleUri)376 std::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
MakeFilePath(const std::string & codePath,const std::string & modulePath,std::string & fileName)409 bool 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
GetLoadPath(const std::string & hapPath)443 std::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
GetRelativePath(const std::string & srcPath)452 std::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