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