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