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
16 #include "file_entry.h"
17 #include <cstring>
18 #include <fstream>
19 #include <iostream>
20 #include "dirent.h"
21 #include "sys/stat.h"
22 #include "unistd.h"
23 #ifdef _WIN32
24 #include "shlwapi.h"
25 #include "windows.h"
26 #endif
27 #include "resource_data.h"
28 #include "restool_errors.h"
29
30 namespace OHOS {
31 namespace Global {
32 namespace Restool {
33 #ifdef _WIN32
34 const std::string FileEntry::SEPARATE = "\\";
35 #else
36 const std::string FileEntry::SEPARATE = "/";
37 #endif
38
39 using namespace std;
FileEntry(const string & path)40 FileEntry::FileEntry(const string &path)
41 : filePath_(path), isFile_(false)
42 {
43 }
44
~FileEntry()45 FileEntry::~FileEntry()
46 {
47 }
48
Init()49 bool FileEntry::Init()
50 {
51 string filePath = filePath_.GetPath();
52 if (!Exist(filePath)) {
53 cerr << "Warning: file not exist: " << filePath << endl;
54 return false;
55 }
56
57 isFile_ = !IsDirectory(filePath);
58 return true;
59 }
60
GetChilds() const61 const vector<unique_ptr<FileEntry>> FileEntry::GetChilds() const
62 {
63 vector<unique_ptr<FileEntry>> children;
64 string filePath = filePath_.GetPath();
65 #ifdef _WIN32
66 WIN32_FIND_DATA findData;
67 string temp(filePath + "\\*.*");
68 HANDLE handle = FindFirstFile(AdaptLongPath(temp).c_str(), &findData);
69 if (handle == INVALID_HANDLE_VALUE) {
70 return children;
71 }
72
73 do {
74 string filename(findData.cFileName);
75 if (IsIgnore(filename)) {
76 continue;
77 }
78
79 filePath = filePath_.GetPath() + SEPARATE + filename;
80 unique_ptr<FileEntry> f = make_unique<FileEntry>(filePath);
81 f->Init();
82 children.push_back(move(f));
83 } while (FindNextFile(handle, &findData));
84 FindClose(handle);
85 #else
86 DIR *handle = opendir(filePath.c_str());
87 struct dirent *entry;
88 while ((entry = readdir(handle)) != nullptr) {
89 string filename(entry->d_name);
90 if (IsIgnore(filename)) {
91 continue;
92 }
93
94 filePath = filePath_.GetPath() + SEPARATE + filename;
95 unique_ptr<FileEntry> f = make_unique<FileEntry>(filePath);
96 f->Init();
97 children.push_back(move(f));
98 }
99 closedir(handle);
100 #endif
101 return children;
102 }
103
IsFile() const104 bool FileEntry::IsFile() const
105 {
106 return isFile_;
107 }
108
GetFilePath() const109 const FileEntry::FilePath &FileEntry::GetFilePath() const
110 {
111 return filePath_;
112 }
113
Exist(const string & path)114 bool FileEntry::Exist(const string &path)
115 {
116 #ifdef _WIN32
117 return PathFileExists(AdaptLongPath(path).c_str());
118 #else
119 struct stat s;
120 if (stat(path.c_str(), &s) != 0) {
121 return false;
122 }
123 #endif
124 return true;
125 }
126
RemoveAllDir(const string & path)127 bool FileEntry::RemoveAllDir(const string &path)
128 {
129 FileEntry f(path);
130 if (!f.Init()) {
131 return false;
132 }
133
134 if (f.IsFile()) {
135 PrintError(GetError(ERR_CODE_REMOVE_FILE_ERROR).FormatCause(path.c_str(), "not directory"));
136 return false;
137 }
138 return RemoveAllDirInner(f);
139 }
140
RemoveFile(const string & path)141 bool FileEntry::RemoveFile(const string &path)
142 {
143 FileEntry f(path);
144 if (!f.Init()) {
145 return false;
146 }
147 return RemoveAllDirInner(f);
148 }
149
CreateDirs(const string & path)150 bool FileEntry::CreateDirs(const string &path)
151 {
152 return CreateDirsInner(path, 0);
153 }
154
CopyFileInner(const string & src,const string & dst)155 bool FileEntry::CopyFileInner(const string &src, const string &dst)
156 {
157 #ifdef _WIN32
158 if (!CopyFile(AdaptLongPath(src).c_str(), AdaptLongPath(dst).c_str(), false)) {
159 PrintError(GetError(ERR_CODE_COPY_FILE_ERROR).FormatCause(src.c_str(), dst.c_str(), strerror(errno)));
160 return false;
161 }
162 #else
163 ifstream in(src, ios::binary);
164 ofstream out(dst, ios::binary);
165 if (!in || !out) {
166 PrintError(GetError(ERR_CODE_COPY_FILE_ERROR).FormatCause(src.c_str(), dst.c_str(), strerror(errno)));
167 return false;
168 }
169 out << in.rdbuf();
170 #endif
171 return true;
172 }
173
IsDirectory(const string & path)174 bool FileEntry::IsDirectory(const string &path)
175 {
176 #ifdef _WIN32
177 if (!PathIsDirectory(AdaptLongPath(path).c_str())) {
178 return false;
179 }
180 return true;
181 #else
182 struct stat s;
183 stat(path.c_str(), &s);
184 return S_ISDIR(s.st_mode);
185 #endif
186 }
187
RealPath(const string & path)188 string FileEntry::RealPath(const string &path)
189 {
190 #ifdef _WIN32
191 char buffer[MAX_PATH];
192 if (!PathCanonicalize(buffer, path.c_str())) {
193 return "";
194 }
195
196 if (PathIsRelative(buffer)) {
197 char current[MAX_PATH];
198 if (!GetCurrentDirectory(MAX_PATH, current)) {
199 return "";
200 }
201
202 char temp[MAX_PATH];
203 if (!PathCombine(temp, current, buffer)) {
204 return "";
205 }
206 if (!Exist(string(temp))) {
207 return "";
208 }
209 return string(temp);
210 }
211 #else
212 char buffer[PATH_MAX];
213 if (!realpath(path.c_str(), buffer)) {
214 return "";
215 }
216 #endif
217 if (!Exist(string(buffer))) {
218 return "";
219 }
220 return string(buffer);
221 }
222
FilePath(const string & path)223 FileEntry::FilePath::FilePath(const string &path) : filePath_(path)
224 {
225 Format();
226 Init();
227 }
228
~FilePath()229 FileEntry::FilePath::~FilePath()
230 {
231 }
232
Append(const string & path)233 FileEntry::FilePath FileEntry::FilePath::Append(const string &path)
234 {
235 Format();
236 string filePath = filePath_ + SEPARATE + path;
237 return FilePath(filePath);
238 }
239
ReplaceExtension(const string & extension)240 FileEntry::FilePath FileEntry::FilePath::ReplaceExtension(const string &extension)
241 {
242 string filePath;
243 if (!parent_.empty()) {
244 filePath += parent_ + SEPARATE;
245 }
246
247 filePath += filename_.substr(0, filename_.length() - extension_.length()) + extension;
248 return FilePath(filePath);
249 }
250
GetParent()251 FileEntry::FilePath FileEntry::FilePath::GetParent()
252 {
253 return FilePath(parent_);
254 }
255
GetPath() const256 const string &FileEntry::FilePath::GetPath() const
257 {
258 return filePath_;
259 }
260
GetFilename() const261 const string &FileEntry::FilePath::GetFilename() const
262 {
263 return filename_;
264 }
265
GetExtension() const266 const string &FileEntry::FilePath::GetExtension() const
267 {
268 return extension_;
269 }
270
GetSegments() const271 const vector<string> FileEntry::FilePath::GetSegments() const
272 {
273 vector<string> segments;
274 string::size_type offset = 0;
275 string::size_type pos = filePath_.find_first_of(SEPARATE.front(), offset);
276 while (pos != string::npos) {
277 segments.push_back(filePath_.substr(offset, pos - offset));
278 offset = pos + 1;
279 pos = filePath_.find_first_of(SEPARATE.front(), offset);
280 }
281
282 if (offset < filePath_.length()) {
283 segments.push_back(filePath_.substr(offset));
284 }
285 return segments;
286 }
287
288 // below private
IsIgnore(const string & filename) const289 bool FileEntry::IsIgnore(const string &filename) const
290 {
291 if (filename == "." || filename == "..") {
292 return true;
293 }
294 return false;
295 }
296
RemoveAllDirInner(const FileEntry & entry)297 bool FileEntry::RemoveAllDirInner(const FileEntry &entry)
298 {
299 string path = entry.GetFilePath().GetPath();
300 if (!Exist(path)) {
301 return true;
302 }
303 if (entry.IsFile()) {
304 #ifdef _WIN32
305 bool result = remove(AdaptLongPath(path).c_str()) == 0;
306 #else
307 bool result = remove(path.c_str()) == 0;
308 #endif
309 if (!result) {
310 PrintError(GetError(ERR_CODE_REMOVE_FILE_ERROR).FormatCause(path.c_str(), strerror(errno)));
311 return false;
312 }
313 return true;
314 }
315
316 for (const auto &iter : entry.GetChilds()) {
317 if (!RemoveAllDirInner(*iter)) {
318 return false;
319 }
320 }
321 #ifdef _WIN32
322 bool result = rmdir(AdaptLongPath(path).c_str()) == 0;
323 #else
324 bool result = rmdir(path.c_str()) == 0;
325 #endif
326 if (!result) {
327 PrintError(GetError(ERR_CODE_REMOVE_FILE_ERROR).FormatCause(path.c_str(), strerror(errno)));
328 return false;
329 }
330 return true;
331 }
332
CreateDirsInner(const string & path,string::size_type offset)333 bool FileEntry::CreateDirsInner(const string &path, string::size_type offset)
334 {
335 string::size_type pos = path.find_first_of(SEPARATE.front(), offset);
336 if (pos == string::npos) {
337 #ifdef _WIN32
338 return CreateDirectory(AdaptLongPath(path).c_str(), nullptr) != 0;
339 #else
340 return mkdir(path.c_str(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == 0;
341 #endif
342 }
343
344 string subPath = path.substr(0, pos + 1);
345 if (!Exist(subPath)) {
346 #ifdef _WIN32
347 if (!CreateDirectory(AdaptLongPath(subPath).c_str(), nullptr)) {
348 #else
349 if (mkdir(subPath.c_str(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) != 0) {
350 #endif
351 return false;
352 }
353 }
354 return CreateDirsInner(path, pos + 1);
355 }
356
357 void FileEntry::FilePath::Format()
358 {
359 if (filePath_.back() != SEPARATE.front()) {
360 return;
361 }
362 filePath_.pop_back();
363 }
364
365 void FileEntry::FilePath::Init()
366 {
367 filename_ = filePath_;
368 string::size_type pos = filePath_.find_last_of(SEPARATE.front());
369 if (pos != string::npos) {
370 parent_ = filePath_.substr(0, pos);
371 if (pos + 1 < filePath_.length()) {
372 filename_ = filePath_.substr(pos + 1);
373 }
374 }
375
376 pos = filename_.find_last_of('.');
377 if (pos != string::npos && pos + 1 < filename_.length()) {
378 extension_ = filename_.substr(pos);
379 }
380 }
381
382 string FileEntry::AdaptLongPath(const string &path)
383 {
384 #ifdef _WIN32
385 if (path.size() >= MAX_PATH -12) { //the max file path can not exceed 260 - 12
386 return LONG_PATH_HEAD + path;
387 }
388 #endif
389 return path;
390 }
391 }
392 }
393 }
394