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