1 /*
2 * Copyright (c) 2022-2024 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 #include <cerrno>
16 #include "file_path.h"
17 #include <sys/stat.h>
18 #include <sys/types.h>
19 #include <unistd.h>
20 #include <dirent.h>
21
22 #include "app_log_wrapper.h"
23 #include "bundle_info.h"
24 #include "common_func.h"
25 #include "directory_ex.h"
26 #include "zip_utils.h"
27
28 using namespace std;
29 using namespace OHOS::AppExecFwk;
30 namespace OHOS {
31 namespace AppExecFwk {
32 namespace LIBZIP {
33 namespace {
34 const std::string SEPARATOR = "/";
35 const std::string ZIP = ".zip";
36 const std::int32_t ZIP_SIZE = 4;
37 const uint8_t SINCE_API_VERSION = 13;
38 constexpr const char* RELATIVE_PATH_SYMBOL = "../";
39 const uint32_t API_VERSION_MOD = 1000;
40 }
41 const FilePath::CharType FilePath::kSeparators[] = FILE_PATH_LITERAL("/");
42 const size_t FilePath::kSeparatorsLength = arraysize(kSeparators);
43 const FilePath::CharType FilePath::kCurrentDirectory[] = FILE_PATH_LITERAL(".");
44 const FilePath::CharType FilePath::kParentDirectory[] = FILE_PATH_LITERAL("..");
45 const FilePath::CharType FilePath::kExtensionSeparator = FILE_PATH_LITERAL('.');
46
FilePath()47 FilePath::FilePath()
48 {}
49
FilePath(const FilePath & that)50 FilePath::FilePath(const FilePath &that) : path_(that.path_)
51 {}
52
FilePath(const std::string & path)53 FilePath::FilePath(const std::string &path) : path_(path)
54 {}
55
~FilePath()56 FilePath::~FilePath()
57 {}
58
operator =(const FilePath & that)59 FilePath &FilePath::operator=(const FilePath &that)
60 {
61 if (&that != this) {
62 path_ = that.path_;
63 }
64 return *this;
65 }
66
operator ==(const FilePath & that) const67 bool FilePath::operator==(const FilePath &that) const
68 {
69 return path_ == that.path_;
70 }
71
operator !=(const FilePath & that) const72 bool FilePath::operator!=(const FilePath &that) const
73 {
74 return path_ != that.path_;
75 }
76
operator <(const FilePath & that) const77 bool FilePath::operator<(const FilePath &that) const
78 {
79 return path_ < that.path_;
80 }
81
FromUTF8Unsafe(const std::string & utf8)82 FilePath FilePath::FromUTF8Unsafe(const std::string &utf8)
83 {
84 return FilePath(utf8);
85 }
86
ReferencesParent()87 bool FilePath::ReferencesParent()
88 {
89 std::vector<std::string> components;
90 GetComponents(components);
91
92 for (size_t i = 0; i < components.size(); i++) {
93 if (components[i].find_first_not_of(FILE_PATH_LITERAL(". \n\r\t")) == std::string::npos &&
94 components[i].find(kParentDirectory) != std::string::npos) {
95 return true;
96 }
97 }
98 return false;
99 }
100
GetComponents(std::vector<std::string> & components)101 void FilePath::GetComponents(std::vector<std::string> &components)
102 {
103 components.clear();
104 if (path_.empty()) {
105 return;
106 }
107
108 FilePath current = *this;
109 FilePath base;
110 // Capture path components.
111 while (current != current.DirName()) {
112 base = current.BaseName();
113 if (!AreAllSeparators(base.path_))
114 components.push_back(base.path_);
115 current = current.DirName();
116 }
117
118 // Capture root, if any.
119 base = current.BaseName();
120 if (!base.path_.empty() && base.path_ != kCurrentDirectory) {
121 components.push_back(current.BaseName().path_);
122 }
123 }
124
DirName()125 FilePath FilePath::DirName()
126 {
127 FilePath newPath(path_);
128 newPath.StripTrailingSeparatorsInternal();
129
130 std::string::size_type lastSeparator =
131 newPath.path_.find_last_of(kSeparators, std::string::npos, kSeparatorsLength - 1);
132 std::string::size_type zero = 0;
133 std::string::size_type one = 1;
134 std::string::size_type two = 2;
135
136 if (lastSeparator == std::string::npos) {
137 // path_ is in the current directory.
138 newPath.path_.resize(zero);
139 } else if (lastSeparator == zero) {
140 // path_ is in the root directory.
141 newPath.path_.resize(one);
142 } else if (lastSeparator == one && IsSeparator(newPath.path_[zero])) {
143 // path_ is in "//" (possibly with a drive letter); leave the double
144 // separator intact indicating alternate root.
145 newPath.path_.resize(two);
146 } else {
147 // path_ is somewhere else, trim the basename.
148 newPath.path_.resize(lastSeparator);
149 }
150
151 newPath.StripTrailingSeparatorsInternal();
152 if (!newPath.path_.length()) {
153 newPath.path_ = kCurrentDirectory;
154 }
155
156 return newPath;
157 }
158
BaseName()159 FilePath FilePath::BaseName()
160 {
161 FilePath newPath(path_);
162 newPath.StripTrailingSeparatorsInternal();
163
164 // Keep everything after the final separator, but if the pathname is only
165 // one character and it's a separator, leave it alone.
166 std::string::size_type lastSeparator =
167 newPath.path_.find_last_of(kSeparators, std::string::npos, kSeparatorsLength - 1);
168 if (lastSeparator != std::string::npos && lastSeparator < newPath.path_.length() - 1) {
169 newPath.path_.erase(0, lastSeparator + 1);
170 }
171
172 return newPath;
173 }
174
StripTrailingSeparatorsInternal()175 void FilePath::StripTrailingSeparatorsInternal()
176 {
177 if (path_.size() == 0) {
178 return;
179 }
180 uint32_t one = 1;
181 uint32_t two = 2;
182 uint32_t start = 1;
183 std::string::size_type lastStripped = std::string::npos;
184 for (std::string::size_type pos = path_.length(); pos > start && FilePath::IsSeparator(path_[pos - one]); --pos) {
185 if (pos != start + one || lastStripped == start + two || !FilePath::IsSeparator(path_[start - one])) {
186 path_.resize(pos - one);
187 lastStripped = pos;
188 }
189 }
190 }
191
AreAllSeparators(const std::string & input)192 bool FilePath::AreAllSeparators(const std::string &input)
193 {
194 for (std::string::const_iterator it = input.begin(); it != input.end(); ++it) {
195 if (!FilePath::IsSeparator(*it)) {
196 return false;
197 }
198 }
199
200 return true;
201 }
202
IsAbsolute()203 bool FilePath::IsAbsolute()
204 {
205 return path_.length() > 0 && FilePath::IsSeparator(path_[0]);
206 }
207
Value()208 std::string FilePath::Value()
209 {
210 return path_;
211 }
IsSeparator(CharType character)212 bool FilePath::IsSeparator(CharType character)
213 {
214 for (size_t i = 0; i < kSeparatorsLength - 1; ++i) {
215 if (character == kSeparators[i]) {
216 return true;
217 }
218 }
219 return false;
220 }
CreateDirectory(const FilePath & fullPath)221 bool FilePath::CreateDirectory(const FilePath &fullPath)
222 {
223 std::vector<FilePath> subpaths;
224
225 // Collect a list of all parent directories.
226 FilePath lastPath = fullPath;
227 subpaths.push_back(fullPath);
228 for (FilePath path = const_cast<FilePath &>(fullPath).DirName(); path.Value() != lastPath.Value();
229 path = path.DirName()) {
230 subpaths.push_back(path);
231 lastPath = path;
232 }
233 mode_t rootMode = 0777;
234 // Iterate through the parents and create the missing ones.
235 for (std::vector<FilePath>::reverse_iterator i = subpaths.rbegin(); i != subpaths.rend(); ++i) {
236 if (DirectoryExists(*i)) {
237 continue;
238 }
239 if (mkdir(i->Value().c_str(), rootMode) == 0) {
240 continue;
241 }
242
243 if (!DirectoryExists(*i)) {
244 return false;
245 }
246 }
247 return true;
248 }
249 // static
DirectoryExists(const FilePath & path)250 bool FilePath::DirectoryExists(const FilePath &path)
251 {
252 struct stat fileInfo;
253 if (stat(const_cast<FilePath &>(path).Value().c_str(), &fileInfo) == 0) {
254 return S_ISDIR(fileInfo.st_mode);
255 }
256
257 APP_LOGD("stat returns an error");
258 return false;
259 }
IsDir(const FilePath & path)260 bool FilePath::IsDir(const FilePath &path)
261 {
262 std::string mPath(const_cast<FilePath &>(path).Value());
263 if (mPath.empty() || mPath == kCurrentDirectory || mPath == kParentDirectory) {
264 return true;
265 } else {
266 struct stat fileInfo;
267 int ret = stat(mPath.c_str(), &fileInfo);
268 return (ret == 0 && S_ISDIR(fileInfo.st_mode));
269 }
270 }
271
PathIsValid(const FilePath & path)272 bool FilePath::PathIsValid(const FilePath &path)
273 {
274 return access(const_cast<FilePath &>(path).Value().c_str(), F_OK) == 0;
275 }
276
PathIsReadable(const FilePath & path)277 bool FilePath::PathIsReadable(const FilePath &path)
278 {
279 return access(const_cast<FilePath &>(path).Value().c_str(), R_OK) == 0;
280 }
281
PathIsWriteable(const FilePath & path)282 bool FilePath::PathIsWriteable(const FilePath &path)
283 {
284 return access(const_cast<FilePath &>(path).Value().c_str(), W_OK) == 0;
285 }
286
287 // Returns a FilePath by appending a separator and the supplied path
288 // component to this object's path. Append takes care to avoid adding
289 // excessive separators if this object's path already ends with a separator.
290 // If this object's path is kCurrentDirectory, a new FilePath corresponding
291 // only to |component| is returned. |component| must be a relative path;
292 // it is an error to pass an absolute path.
Append(const std::string & component)293 FilePath FilePath::Append(const std::string &component)
294 {
295 if (component.empty()) {
296 FilePath pathOrg(path_);
297 return pathOrg;
298 }
299
300 std::string newPathString;
301 // empty
302 if (Value().empty()) {
303 newPathString += component;
304 } else if (EndsWith(Value(), SEPARATOR)) {
305 if (StartsWith(component, SEPARATOR)) {
306 newPathString = component.substr(1, component.size());
307 } else {
308 newPathString = path_ + SEPARATOR + component;
309 }
310 } else {
311 if (StartsWith(component, SEPARATOR)) {
312 newPathString = path_ + component;
313 } else {
314 newPathString = path_ + SEPARATOR + component;
315 }
316 }
317
318 FilePath newPath(newPathString);
319 return newPath;
320 }
321
Append(FilePath & component)322 FilePath FilePath::Append(FilePath &component)
323 {
324 if (component.path_.empty()) {
325 FilePath pathOrg(path_);
326 return pathOrg;
327 }
328
329 return Append(component.path_);
330 }
331
AppendSeparator(void)332 void FilePath::AppendSeparator(void)
333 {
334 if (path_.empty()) {
335 path_ = SEPARATOR;
336 } else {
337 path_ += SEPARATOR;
338 }
339 }
340 // If IsParent(child) holds, appends to path (if non-NULL) the
341 // relative path to child and returns true.
AppendRelativePath(const FilePath & child,FilePath * path)342 bool FilePath::AppendRelativePath(const FilePath &child, FilePath *path)
343 {
344 FilePath childPath = child;
345 std::vector<std::string> parentComponents;
346 std::vector<std::string> childComponents;
347 GetComponents(parentComponents);
348 childPath.GetComponents(childComponents);
349
350 if (parentComponents.empty() || parentComponents.size() >= childComponents.size()) {
351 return false;
352 }
353
354 std::vector<std::string> parentComponentsReverse;
355 std::vector<std::string> childComponentsReverse;
356
357 std::vector<std::string>::reverse_iterator riter;
358 for (riter = parentComponents.rbegin(); riter != parentComponents.rend(); ++riter) {
359 parentComponentsReverse.push_back(*riter);
360 }
361 for (riter = childComponents.rbegin(); riter != childComponents.rend(); ++riter) {
362 childComponentsReverse.push_back(*riter);
363 }
364 std::vector<std::string>::const_iterator parentIt = parentComponentsReverse.begin();
365 std::vector<std::string>::const_iterator childIt = childComponentsReverse.begin();
366 while (parentIt != parentComponentsReverse.end()) {
367 if (*parentIt != *childIt)
368 return false;
369 ++parentIt;
370 ++childIt;
371 }
372
373 if (path != nullptr) {
374 // Relative paths do not include separator
375 if ((childIt != childComponentsReverse.end()) && (*childIt == SEPARATOR)) {
376 ++childIt;
377 }
378
379 for (; childIt != childComponentsReverse.end(); ++childIt) {
380 *path = path->Append(*childIt);
381 }
382 }
383 return true;
384 }
GetZipAllDirFiles(const std::string & path,std::vector<std::string> & files)385 bool FilePath::GetZipAllDirFiles(const std::string &path, std::vector<std::string> &files)
386 {
387 std::string pathStringWithDelimiter;
388 if (path.empty() || path == kCurrentDirectory || path == kParentDirectory) {
389 return true;
390 }
391 DIR *dir = opendir(path.c_str());
392 if (dir == nullptr) {
393 APP_LOGE("fail to stat errno:%{public}d", errno);
394 return false;
395 }
396
397 bool result = false;
398 while (true) {
399 result = true;
400 struct dirent *ptr = readdir(dir);
401 if (ptr == nullptr) {
402 break;
403 }
404 // current dir OR parent dir
405 if ((strcmp(ptr->d_name, kCurrentDirectory) == 0) || (strcmp(ptr->d_name, kParentDirectory) == 0)) {
406 continue;
407 } else if (ptr->d_type == DT_DIR) {
408 pathStringWithDelimiter = IncludeTrailingPathDelimiter(path) + string(ptr->d_name);
409 std::vector<std::string> itemFiles;
410 GetZipAllDirFiles(pathStringWithDelimiter, itemFiles);
411
412 if (itemFiles.empty()) {
413 files.push_back(pathStringWithDelimiter);
414 } else {
415 files.insert(files.end(), itemFiles.begin(), itemFiles.end());
416 }
417 } else {
418 files.push_back(IncludeTrailingPathDelimiter(path) + string(ptr->d_name));
419 }
420 }
421 closedir(dir);
422 return result;
423 }
424
CheckDestDirTail()425 std::string FilePath::CheckDestDirTail()
426 {
427 if (path_.substr(path_.size()-ZIP_SIZE, ZIP_SIZE) == ZIP) {
428 return path_;
429 } else {
430 return path_ + ZIP;
431 }
432 }
433
IsNeedCheckFilePathBaseOnAPIVersion()434 bool FilePath::IsNeedCheckFilePathBaseOnAPIVersion()
435 {
436 auto bundleMgr = CommonFunc::GetBundleMgr();
437 if (bundleMgr == nullptr) {
438 APP_LOGE("bundleMgr is nullptr");
439 return false;
440 }
441 BundleInfo bundleInfo;
442 auto ret = bundleMgr->GetBundleInfoForSelf(0, bundleInfo);
443 if (ret != ERR_OK) {
444 APP_LOGE("get failed, ret:%{public}d", ret);
445 return false;
446 }
447 return (bundleInfo.targetVersion % API_VERSION_MOD) >= SINCE_API_VERSION;
448 }
449
HasRelativePathBaseOnAPIVersion(const std::string & path)450 bool FilePath::HasRelativePathBaseOnAPIVersion(const std::string &path)
451 {
452 if (!IsNeedCheckFilePathBaseOnAPIVersion()) {
453 return false;
454 }
455 if (path.find(RELATIVE_PATH_SYMBOL) != string::npos) {
456 APP_LOGI("path constains ../");
457 return true;
458 }
459 return false;
460 }
461
HasRelativePathBaseOnAPIVersion(const std::vector<std::string> & paths)462 bool FilePath::HasRelativePathBaseOnAPIVersion(const std::vector<std::string> &paths)
463 {
464 if (!IsNeedCheckFilePathBaseOnAPIVersion()) {
465 return false;
466 }
467 for (const auto &path : paths) {
468 if (path.find(RELATIVE_PATH_SYMBOL) != string::npos) {
469 APP_LOGI("path constains ../");
470 return true;
471 }
472 }
473 return false;
474 }
475 } // namespace LIBZIP
476 } // namespace AppExecFwk
477 } // namespace OHOS
478