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 fileLen = stream.tellg();
238 if (fileLen >= PATH_MAX) {
239 return std::string();
240 }
241
242 stream.seekg(0);
243 stream.read(path, fileLen);
244 path[fileLen] = '\0';
245 stream.close();
246
247 std::string npmPackagePath = npmPath + '/' + StripString(path);
248 if (npmPackagePath.size() >= PATH_MAX) {
249 return std::string();
250 }
251 if (realpath(npmPackagePath.c_str(), path) == nullptr) {
252 return std::string();
253 }
254 return path;
255 }
256
FindNpmPackageInTopLevel(const std::string & moduleInstallPath,const std::string & npmPackage,size_t start)257 std::string FindNpmPackageInTopLevel(
258 const std::string& moduleInstallPath, const std::string& npmPackage, size_t start)
259 {
260 for (size_t level = start; level <= MAX_NPM_LEVEL; ++level) {
261 std::string path = moduleInstallPath + NPM_PATH_SEGMENT + '/' + std::to_string(level) + '/' + npmPackage;
262 path = FindNpmPackageInPath(path);
263 if (!path.empty()) {
264 return path;
265 }
266 }
267
268 return std::string();
269 }
270
FindNpmPackage(const std::string & curJsModulePath,const std::string & npmPackage)271 std::string FindNpmPackage(const std::string& curJsModulePath, const std::string& npmPackage)
272 {
273 std::string newJsModulePath = MakeNewJsModulePath(curJsModulePath, npmPackage);
274 if (!newJsModulePath.empty()) {
275 return newJsModulePath;
276 }
277 std::string moduleInstallPath = GetInstallPath(curJsModulePath);
278 if (moduleInstallPath.empty()) {
279 return std::string();
280 }
281 std::vector<std::string> pathVector;
282 SplitString(curJsModulePath, pathVector, moduleInstallPath.length());
283 if (pathVector.empty()) {
284 return std::string();
285 }
286
287 if (pathVector[0] != NPM_PATH_SEGMENT) {
288 return FindNpmPackageInTopLevel(moduleInstallPath, npmPackage);
289 }
290
291 // Remove file name, reserve only dir name
292 pathVector.pop_back();
293
294 // Find npm package until reach top level npm path such as 'node_modules/0',
295 // so there must be 2 element in vector
296 while (pathVector.size() > 2) {
297 std::string path =
298 moduleInstallPath + JoinString(pathVector, '/') + '/' + NPM_PATH_SEGMENT + '/' + npmPackage;
299 path = FindNpmPackageInPath(path);
300 if (!path.empty()) {
301 return path;
302 }
303
304 pathVector.pop_back();
305 }
306
307 char* p = nullptr;
308 size_t index = std::strtoul(pathVector.back().c_str(), &p, 10);
309 if (p == nullptr || *p != '\0') {
310 return std::string();
311 }
312
313 return FindNpmPackageInTopLevel(moduleInstallPath, npmPackage, index);
314 }
315
ParseOhmUri(const std::string & originBundleName,const std::string & curJsModulePath,const std::string & newJsModuleUri)316 std::string ParseOhmUri(
317 const std::string& originBundleName, const std::string& curJsModulePath, const std::string& newJsModuleUri)
318 {
319 std::string moduleInstallPath;
320 std::vector<std::string> pathVector;
321 size_t index = 0;
322
323 if (StringStartWith(newJsModuleUri, PREFIX_BUNDLE, sizeof(PREFIX_BUNDLE) - 1)) {
324 SplitString(newJsModuleUri, pathVector, sizeof(PREFIX_BUNDLE) - 1);
325
326 // Uri should have atleast 3 segments
327 if (pathVector.size() < SEGMENTS_LIMIT_THREE) {
328 return std::string();
329 }
330
331 const auto& bundleName = pathVector[index++];
332 if (bundleName == originBundleName) {
333 moduleInstallPath = std::string(BUNDLE_INSTALL_PATH);
334 } else {
335 moduleInstallPath = std::string(OTHER_BUNDLE_INSTALL_PATH);
336 moduleInstallPath.append(bundleName).append("/");
337 }
338 moduleInstallPath.append(pathVector[index++]).append("/");
339 } else if (StringStartWith(newJsModuleUri, PREFIX_MODULE, sizeof(PREFIX_MODULE) - 1)) {
340 SplitString(newJsModuleUri, pathVector, sizeof(PREFIX_MODULE) - 1);
341
342 // Uri should have atleast 2 segments
343 if (pathVector.size() < SEGMENTS_LIMIT_TWO) {
344 return std::string();
345 }
346
347 moduleInstallPath = GetInstallPath(curJsModulePath, false);
348 if (moduleInstallPath.empty()) {
349 return std::string();
350 }
351 moduleInstallPath.append(pathVector[index++]).append("/");
352 } else if (StringStartWith(newJsModuleUri, PREFIX_LOCAL, sizeof(PREFIX_LOCAL) - 1)) {
353 SplitString(newJsModuleUri, pathVector, sizeof(PREFIX_LOCAL) - 1);
354
355 if (pathVector.empty()) {
356 return std::string();
357 }
358
359 moduleInstallPath = GetInstallPath(curJsModulePath);
360 if (moduleInstallPath.empty()) {
361 return std::string();
362 }
363 } else {
364 return std::string();
365 }
366
367 if (pathVector[index] != NPM_PATH_SEGMENT) {
368 return moduleInstallPath + JoinString(pathVector, '/', index);
369 }
370
371 return FindNpmPackageInTopLevel(moduleInstallPath, JoinString(pathVector, '/', index + 1));
372 }
373
NormalizeUri(const std::string & bundleName,const std::string & curJsModulePath,const std::string & newJsModuleUri)374 std::string NormalizeUri(
375 const std::string& bundleName, const std::string& curJsModulePath, const std::string& newJsModuleUri)
376 {
377 std::string newJsModulePath;
378 if (curJsModulePath.empty() || newJsModuleUri.empty()) {
379 return newJsModulePath;
380 }
381
382 std::string normalizeUri = newJsModuleUri;
383 std::replace(normalizeUri.begin(), normalizeUri.end(), '\\', '/');
384
385 switch (normalizeUri[0]) {
386 case '.': {
387 newJsModulePath = MakeNewJsModulePath(curJsModulePath, normalizeUri);
388 break;
389 }
390 case '@': {
391 newJsModulePath = ParseOhmUri(bundleName, curJsModulePath, normalizeUri);
392 if (newJsModulePath.empty()) {
393 newJsModulePath = FindNpmPackage(curJsModulePath, normalizeUri);
394 }
395 break;
396 }
397 default: {
398 newJsModulePath = FindNpmPackage(curJsModulePath, normalizeUri);
399 break;
400 }
401 }
402
403 FixExtName(newJsModulePath);
404 return newJsModulePath;
405 }
406
MakeFilePath(const std::string & codePath,const std::string & modulePath,std::string & fileName)407 bool MakeFilePath(const std::string& codePath, const std::string& modulePath, std::string& fileName)
408 {
409 std::string path(codePath);
410 path.append("/").append(modulePath);
411 if (path.length() > PATH_MAX) {
412 return false;
413 }
414 char resolvedPath[PATH_MAX + 1] = { 0 };
415 if (realpath(path.c_str(), resolvedPath) != nullptr) {
416 fileName = resolvedPath;
417 return true;
418 }
419
420 auto start = path.find_last_of('/');
421 auto end = path.find_last_of('.');
422 if (end == std::string::npos || end == 0) {
423 return false;
424 }
425
426 auto pos = path.find_last_of('.', end - 1);
427 if (pos == std::string::npos) {
428 return false;
429 }
430
431 path.erase(start + 1, pos - start);
432
433 if (realpath(path.c_str(), resolvedPath) == nullptr) {
434 return false;
435 }
436
437 fileName = resolvedPath;
438 return true;
439 }
440
GetLoadPath(const std::string & hapPath)441 std::string GetLoadPath(const std::string& hapPath)
442 {
443 std::regex hapPattern(std::string(Constants::ABS_CODE_PATH) + std::string(Constants::FILE_SEPARATOR));
444 std::string loadPath = std::regex_replace(hapPath, hapPattern, "");
445 loadPath = std::string(Constants::LOCAL_CODE_PATH) + std::string(Constants::FILE_SEPARATOR) +
446 loadPath.substr(loadPath.find(std::string(Constants::FILE_SEPARATOR)) + 1);
447 return loadPath;
448 }
449
GetRelativePath(const std::string & srcPath)450 std::string GetRelativePath(const std::string& srcPath)
451 {
452 if (srcPath.empty() || srcPath[0] != '/') {
453 return srcPath;
454 }
455 std::regex srcPattern(Constants::LOCAL_CODE_PATH);
456 std::string relativePath = std::regex_replace(srcPath, srcPattern, "");
457 if (relativePath.find(Constants::FILE_SEPARATOR) == 0) {
458 relativePath = relativePath.substr(1);
459 relativePath = relativePath.substr(relativePath.find(std::string(Constants::FILE_SEPARATOR)) + 1);
460 }
461 return relativePath;
462 }
463 } // namespace AbilityBase
464 } // namespace OHOS
465