1 /*
2 * Copyright (c) 2021 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 "directory_ex.h"
17 #include <dirent.h>
18 #include <fcntl.h>
19 #include <stack>
20 #include "securec.h"
21 #include "unistd.h"
22 #include "utils_log.h"
23 using namespace std;
24
25 namespace OHOS {
26
27 #ifdef UTILS_CXX_RUST
RustGetCurrentProcFullFileName()28 rust::String RustGetCurrentProcFullFileName()
29 {
30 return rust::String(GetCurrentProcFullFileName());
31 }
32
RustGetCurrentProcPath()33 rust::String RustGetCurrentProcPath()
34 {
35 return rust::String(GetCurrentProcPath());
36 }
37
RustExtractFilePath(const rust::String & fileFullName)38 rust::String RustExtractFilePath(const rust::String& fileFullName)
39 {
40 std::string tmpName = std::string(fileFullName);
41 return rust::String(ExtractFilePath(tmpName));
42 }
43
RustExtractFileName(const rust::String & fileFullName)44 rust::String RustExtractFileName(const rust::String& fileFullName)
45 {
46 std::string tmpName = std::string(fileFullName);
47 return rust::String(ExtractFileName(tmpName));
48 }
49
RustExtractFileExt(const rust::String & fileName)50 rust::String RustExtractFileExt(const rust::String& fileName)
51 {
52 std::string tmpName = std::string(fileName);
53 return rust::String(ExtractFileExt(tmpName));
54 }
55
RustExcludeTrailingPathDelimiter(const rust::String & path)56 rust::String RustExcludeTrailingPathDelimiter(const rust::String& path)
57 {
58 std::string tmpPath = std::string(path);
59 return rust::String(ExcludeTrailingPathDelimiter(tmpPath));
60 }
61
RustIncludeTrailingPathDelimiter(const rust::String & path)62 rust::String RustIncludeTrailingPathDelimiter(const rust::String& path)
63 {
64 std::string tmpPath = std::string(path);
65 return rust::String(IncludeTrailingPathDelimiter(tmpPath));
66 }
67
RustPathToRealPath(const rust::String & path,rust::String & realPath)68 bool RustPathToRealPath(const rust::String& path, rust::String& realPath)
69 {
70 std::string tmpPath = std::string(path);
71 std::string tmpResolved;
72
73 if (PathToRealPath(tmpPath, tmpResolved)) {
74 realPath = tmpResolved;
75 return true;
76 }
77
78 return false;
79 }
80
RustGetDirFiles(const rust::String & path,rust::vec<rust::String> & files)81 void RustGetDirFiles(const rust::String& path, rust::vec<rust::String>& files)
82 {
83 std::string tmpPath(path);
84 std::vector<std::string> tmpFiles(files.begin(), files.end());
85 GetDirFiles(tmpPath, tmpFiles);
86 std::copy(tmpFiles.begin(), tmpFiles.end(), std::back_inserter(files));
87 }
88 #endif
89
GetCurrentProcFullFileName()90 string GetCurrentProcFullFileName()
91 {
92 char procFile[PATH_MAX + 1] = {0};
93 int ret = readlink("/proc/self/exe", procFile, PATH_MAX);
94 if (ret < 0 || ret > PATH_MAX) {
95 UTILS_LOGD("Get proc name failed, ret is: %{public}d!", ret);
96 return string();
97 }
98 procFile[ret] = '\0';
99 return string(procFile);
100 }
101
GetCurrentProcPath()102 string GetCurrentProcPath()
103 {
104 return ExtractFilePath(GetCurrentProcFullFileName());
105 }
106
ExtractFilePath(const string & fileFullName)107 string ExtractFilePath(const string& fileFullName)
108 {
109 return string(fileFullName).substr(0, fileFullName.rfind("/") + 1);
110 }
111
ExtractFileName(const std::string & fileFullName)112 std::string ExtractFileName(const std::string& fileFullName)
113 {
114 return string(fileFullName).substr(fileFullName.rfind("/") + 1, fileFullName.size());
115 }
116
ExtractFileExt(const string & fileName)117 string ExtractFileExt(const string& fileName)
118 {
119 string::size_type pos = fileName.rfind(".");
120 if (pos == string::npos) {
121 return "";
122 }
123
124 return string(fileName).substr(pos + 1, fileName.size());
125 }
126
ExcludeTrailingPathDelimiter(const std::string & path)127 string ExcludeTrailingPathDelimiter(const std::string& path)
128 {
129 if (path.rfind("/") != path.size() - 1) {
130 return path;
131 }
132
133 if (!path.empty()) {
134 return path.substr(0, (int)path.size() - 1);
135 }
136
137 return path;
138 }
139
IncludeTrailingPathDelimiter(const std::string & path)140 string IncludeTrailingPathDelimiter(const std::string& path)
141 {
142 if (path.empty()) {
143 return "/";
144 }
145 if (path.rfind("/") != path.size() - 1) {
146 return path + "/";
147 }
148
149 return path;
150 }
151
GetDirFiles(const string & path,vector<string> & files)152 void GetDirFiles(const string& path, vector<string>& files)
153 {
154 DIR *dir = opendir(path.c_str());
155 if (dir == nullptr) {
156 UTILS_LOGD("Failed to open root dir: %{public}s: %{public}s ", path.c_str(), strerror(errno));
157 return;
158 }
159
160 string currentPath = ExcludeTrailingPathDelimiter(path);
161 stack<DIR *> traverseStack;
162 traverseStack.push(dir);
163 while (!traverseStack.empty()) {
164 DIR *topNode = traverseStack.top();
165 dirent *ptr = readdir(topNode);
166 if (ptr == nullptr) {
167 closedir(topNode);
168 traverseStack.pop();
169 auto pos = currentPath.find_last_of("/");
170 if (pos != string::npos) {
171 currentPath.erase(pos);
172 }
173 continue;
174 }
175
176 string name = ptr->d_name;
177 if (name == "." || name == "..") {
178 continue;
179 }
180 if (ptr->d_type == DT_DIR) {
181 int currentFd = dirfd(topNode);
182 if (currentFd < 0) {
183 UTILS_LOGD("Failed to get dirfd, fd: %{public}d: %{public}s ", currentFd, strerror(errno));
184 continue;
185 }
186 int subFd = openat(currentFd, name.c_str(), O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
187 if (subFd < 0) {
188 UTILS_LOGD("Failed in subFd openat: %{public}s ", name.c_str());
189 continue;
190 }
191 DIR *subDir = fdopendir(subFd);
192 if (subDir == nullptr) {
193 close(subFd);
194 UTILS_LOGD("Failed in fdopendir: %{public}s", strerror(errno));
195 continue;
196 }
197 traverseStack.push(subDir);
198 currentPath = IncludeTrailingPathDelimiter(currentPath) + name;
199 } else {
200 files.push_back(IncludeTrailingPathDelimiter(currentPath) + name);
201 }
202 }
203 }
204
ForceCreateDirectory(const string & path)205 bool ForceCreateDirectory(const string& path)
206 {
207 string::size_type index = 0;
208 do {
209 string subPath;
210 index = path.find('/', index + 1);
211 if (index == string::npos) {
212 subPath = path;
213 } else {
214 subPath = path.substr(0, index);
215 }
216
217 if (access(subPath.c_str(), F_OK) != 0) {
218 if (mkdir(subPath.c_str(), (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)) != 0 && errno != EEXIST) {
219 return false;
220 }
221 }
222 } while (index != string::npos);
223
224 return access(path.c_str(), F_OK) == 0;
225 }
226
227 struct DirectoryNode {
228 DIR *dir;
229 int currentFd;
230 char name[256]; // the same max char length with d_name in struct dirent
231 };
232
ForceRemoveDirectory(const string & path)233 bool ForceRemoveDirectory(const string& path)
234 {
235 bool ret = true;
236 int strRet;
237 DIR *dir = opendir(path.c_str());
238 if (dir == nullptr) {
239 UTILS_LOGE("Failed to open root dir: %{public}s: %{public}s ", path.c_str(), strerror(errno));
240 return false;
241 }
242 stack<DIR *> traversStack;
243 stack<DirectoryNode> removeStack;
244 traversStack.push(dir);
245 while (!traversStack.empty()) {
246 DIR *currentDir = traversStack.top();
247 traversStack.pop();
248 DirectoryNode node;
249 int currentFd = dirfd(currentDir);
250 if (currentFd < 0) {
251 UTILS_LOGE("Failed to get dirfd, fd: %{public}d: %{public}s ", currentFd, strerror(errno));
252 ret = false;
253 continue;
254 }
255
256 while (true) {
257 struct dirent *ptr = readdir(currentDir);
258 if (ptr == nullptr) {
259 break;
260 }
261 const char *name = ptr->d_name;
262 // current dir or parent dir
263 if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
264 continue;
265 }
266
267 if (ptr->d_type == DT_DIR) {
268 int subFd = openat(currentFd, name, O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
269 if (subFd < 0) {
270 UTILS_LOGE("Failed in subFd openat: %{public}s ", name);
271 ret = false;
272 continue;
273 }
274 DIR *subDir = fdopendir(subFd);
275 if (subDir == nullptr) {
276 close(subFd);
277 UTILS_LOGE("Failed in fdopendir: %{public}s", strerror(errno));
278 ret = false;
279 continue;
280 }
281 node.dir = subDir;
282 node.currentFd = currentFd;
283 strRet = strcpy_s(node.name, sizeof(node.name), name);
284 if (strRet != EOK) {
285 UTILS_LOGE("Failed to exec strcpy_s, name= %{public}s, strRet= %{public}d", name, strRet);
286 }
287 removeStack.push(node);
288 traversStack.push(subDir);
289 } else {
290 if (faccessat(currentFd, name, F_OK, AT_SYMLINK_NOFOLLOW) == 0) {
291 if (unlinkat(currentFd, name, 0) < 0) {
292 UTILS_LOGE("Couldn't unlinkat subFile %{public}s: %{public}s", name, strerror(errno));
293 ret = false;
294 break;
295 }
296 } else {
297 UTILS_LOGE("Access to file: %{public}s is failed", name);
298 ret = false;
299 break;
300 }
301 }
302 }
303 }
304 if (!ret) {
305 UTILS_LOGD("Failed to remove some subfile under path: %{public}s", path.c_str());
306 }
307 while (!removeStack.empty()) {
308 DirectoryNode node = removeStack.top();
309 removeStack.pop();
310 closedir(node.dir);
311 if (unlinkat(node.currentFd, node.name, AT_REMOVEDIR) < 0) {
312 UTILS_LOGE("Couldn't unlinkat subDir %{public}s: %{public}s", node.name, strerror(errno));
313 continue;
314 }
315 }
316 closedir(dir);
317 if (faccessat(AT_FDCWD, path.c_str(), F_OK, AT_SYMLINK_NOFOLLOW) == 0) {
318 if (remove(path.c_str()) != 0) {
319 UTILS_LOGE("Failed to remove root dir: %{public}s: %{public}s ", path.c_str(), strerror(errno));
320 return false;
321 }
322 }
323 return faccessat(AT_FDCWD, path.c_str(), F_OK, AT_SYMLINK_NOFOLLOW) != 0;
324 }
325
ForceRemoveDirectoryInternal(DIR * dir)326 bool ForceRemoveDirectoryInternal(DIR *dir)
327 {
328 bool ret = true;
329 int rootFd = dirfd(dir);
330 if (rootFd < 0) {
331 UTILS_LOGE("BMS: Failed to get dirfd, fd: %{public}d: %{public}s ", rootFd, strerror(errno));
332 return false;
333 }
334
335 while (true) {
336 struct dirent *ptr = readdir(dir);
337 if (ptr == nullptr) {
338 break;
339 }
340 const char *name = ptr->d_name;
341
342 // current dir or parent dir
343 if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
344 continue;
345 }
346
347 if (ptr->d_type == DT_DIR) {
348 int subFd = openat(rootFd, name, O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
349 if (subFd < 0) {
350 UTILS_LOGE("BMS: Failed in subFd openat: %{public}s, errno: %{public}s ", name, strerror(errno));
351 ret = false;
352 continue;
353 }
354 DIR *subDir = fdopendir(subFd);
355 if (subDir == nullptr) {
356 close(subFd);
357 UTILS_LOGE("BMS: Failed in fdopendir: %{public}s", strerror(errno));
358 ret = false;
359 continue;
360 }
361 ret = ForceRemoveDirectoryInternal(subDir);
362 closedir(subDir);
363 if (unlinkat(rootFd, name, AT_REMOVEDIR) < 0) {
364 UTILS_LOGE("BMS: Couldn't unlinkat subDir %{public}s: %{public}s", name, strerror(errno));
365 ret = false;
366 continue;
367 }
368 } else {
369 if (faccessat(rootFd, name, F_OK, AT_SYMLINK_NOFOLLOW) == 0) {
370 if (unlinkat(rootFd, name, 0) < 0) {
371 UTILS_LOGE("BMS: Couldn't unlinkat subFile %{public}s: %{public}s", name, strerror(errno));
372 return false;
373 }
374 } else {
375 UTILS_LOGE("BMS: Access to file: %{public}s is failed, errno: %{public}s", name, strerror(errno));
376 return false;
377 }
378 }
379 }
380
381 return ret;
382 }
383
ForceRemoveDirectoryBMS(const string & path)384 bool ForceRemoveDirectoryBMS(const string& path)
385 {
386 bool ret = true;
387 DIR *dir = opendir(path.c_str());
388 if (dir == nullptr) {
389 UTILS_LOGE("BMS: Failed to open root dir: %{public}s: %{public}s ", path.c_str(), strerror(errno));
390 return false;
391 }
392 ret = ForceRemoveDirectoryInternal(dir);
393 if (!ret) {
394 UTILS_LOGE("BMS: Failed to remove some subfile under path: %{public}s", path.c_str());
395 }
396 closedir(dir);
397 if (faccessat(AT_FDCWD, path.c_str(), F_OK, AT_SYMLINK_NOFOLLOW) == 0) {
398 if (remove(path.c_str()) != 0) {
399 UTILS_LOGE("BMS: Failed to remove root dir: %{public}s: %{public}s ", path.c_str(), strerror(errno));
400 return false;
401 }
402 }
403
404 return faccessat(AT_FDCWD, path.c_str(), F_OK, AT_SYMLINK_NOFOLLOW) != 0;
405 }
406
RemoveFile(const string & fileName)407 bool RemoveFile(const string& fileName)
408 {
409 if (access(fileName.c_str(), F_OK) == 0) {
410 return remove(fileName.c_str()) == 0;
411 }
412
413 return true;
414 }
415
IsEmptyFolder(const string & path)416 bool IsEmptyFolder(const string& path)
417 {
418 vector<string> files;
419 GetDirFiles(path, files);
420 return files.empty();
421 }
422
GetFolderSize(const string & path)423 uint64_t GetFolderSize(const string& path)
424 {
425 vector<string> files;
426 struct stat statbuf = {0};
427 GetDirFiles(path, files);
428 uint64_t totalSize = 0;
429 for (auto& file : files) {
430 if (stat(file.c_str(), &statbuf) == 0) {
431 totalSize += statbuf.st_size;
432 }
433 }
434
435 return totalSize;
436 }
437
438 // inner function, and param is legitimate
ChangeMode(const string & fileName,const mode_t & mode)439 bool ChangeMode(const string& fileName, const mode_t& mode)
440 {
441 return (chmod(fileName.c_str(), mode) == 0);
442 }
443
ChangeModeFile(const string & fileName,const mode_t & mode)444 bool ChangeModeFile(const string& fileName, const mode_t& mode)
445 {
446 if (access(fileName.c_str(), F_OK) != 0) {
447 return false;
448 }
449
450 return ChangeMode(fileName, mode);
451 }
452
ChangeModeDirectory(const string & path,const mode_t & mode)453 bool ChangeModeDirectory(const string& path, const mode_t& mode)
454 {
455 string subPath;
456 bool ret = true;
457 DIR *dir = opendir(path.c_str());
458 if (dir == nullptr) {
459 return false;
460 }
461
462 while (true) {
463 struct dirent *ptr = readdir(dir);
464 if (ptr == nullptr) {
465 break;
466 }
467
468 // current dir or parent dir
469 if (strcmp(ptr->d_name, ".") == 0 || strcmp(ptr->d_name, "..") == 0) {
470 continue;
471 }
472 subPath = IncludeTrailingPathDelimiter(path) + string(ptr->d_name);
473 if (ptr->d_type == DT_DIR) {
474 ret = ChangeModeDirectory(subPath, mode);
475 } else {
476 if (access(subPath.c_str(), F_OK) == 0) {
477 if (!ChangeMode(subPath, mode)) {
478 UTILS_LOGD("Failed to exec ChangeMode");
479 closedir(dir);
480 return false;
481 }
482 }
483 }
484 }
485 closedir(dir);
486 string currentPath = ExcludeTrailingPathDelimiter(path);
487 if (access(currentPath.c_str(), F_OK) == 0) {
488 if (!ChangeMode(currentPath, mode)) {
489 UTILS_LOGD("Failed to exec ChangeMode");
490 return false;
491 }
492 }
493 return ret;
494 }
495
PathToRealPath(const string & path,string & realPath)496 bool PathToRealPath(const string& path, string& realPath)
497 {
498 if (path.empty()) {
499 UTILS_LOGD("path is empty!");
500 return false;
501 }
502
503 if ((path.length() >= PATH_MAX)) {
504 UTILS_LOGD("path len is error, the len is: [%{public}zu]", path.length());
505 return false;
506 }
507
508 char tmpPath[PATH_MAX] = {0};
509 if (realpath(path.c_str(), tmpPath) == nullptr) {
510 UTILS_LOGE("path (%{public}s) to realpath error: %{public}s", path.c_str(), strerror(errno));
511 return false;
512 }
513
514 realPath = tmpPath;
515 if (access(realPath.c_str(), F_OK) != 0) {
516 UTILS_LOGE("check realpath (%{private}s) error: %{public}s", realPath.c_str(), strerror(errno));
517 return false;
518 }
519 return true;
520 }
521
522 #if defined(IOS_PLATFORM) || defined(_WIN32)
TransformFileName(const string & fileName)523 string TransformFileName(const string& fileName)
524 {
525 string::size_type pos = fileName.find(".");
526 string transformfileName = "";
527 if (pos == string::npos) {
528 transformfileName = fileName;
529
530 #ifdef _WIN32
531 transformfileName = transformfileName.append(".dll");
532 #elif defined IOS_PLATFORM
533 transformfileName = transformfileName.append(".dylib");
534 #endif
535
536 return transformfileName;
537 } else {
538 transformfileName = string(fileName).substr(0, pos + 1);
539
540 #ifdef _WIN32
541 transformfileName = transformfileName.append("dll");
542 #elif defined IOS_PLATFORM
543 transformfileName = transformfileName.append("dylib");
544 #endif
545
546 return transformfileName;
547 }
548 }
549 #endif
550
551 } // OHOS
552