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