• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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