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