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.rfind("/") != path.size() - 1) {
144 return path + "/";
145 }
146
147 return path;
148 }
149
GetDirFiles(const string & path,vector<string> & files)150 void GetDirFiles(const string& path, vector<string>& files)
151 {
152 string pathStringWithDelimiter;
153 DIR *dir = opendir(path.c_str());
154 if (dir == nullptr) {
155 return;
156 }
157
158 while (true) {
159 struct dirent *ptr = readdir(dir);
160 if (ptr == nullptr) {
161 break;
162 }
163
164 // current dir or parent dir
165 if ((strcmp(ptr->d_name, ".") == 0) || (strcmp(ptr->d_name, "..") == 0)) {
166 continue;
167 } else if (ptr->d_type == DT_DIR) {
168 pathStringWithDelimiter = IncludeTrailingPathDelimiter(path) + string(ptr->d_name);
169 GetDirFiles(pathStringWithDelimiter, files);
170 } else {
171 files.push_back(IncludeTrailingPathDelimiter(path) + string(ptr->d_name));
172 }
173 }
174 closedir(dir);
175 }
176
ForceCreateDirectory(const string & path)177 bool ForceCreateDirectory(const string& path)
178 {
179 string::size_type index = 0;
180 do {
181 string subPath;
182 index = path.find('/', index + 1);
183 if (index == string::npos) {
184 subPath = path;
185 } else {
186 subPath = path.substr(0, index);
187 }
188
189 if (access(subPath.c_str(), F_OK) != 0) {
190 if (mkdir(subPath.c_str(), (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)) != 0 && errno != EEXIST) {
191 return false;
192 }
193 }
194 } while (index != string::npos);
195
196 return access(path.c_str(), F_OK) == 0;
197 }
198
199 struct DirectoryNode {
200 DIR *dir;
201 int currentFd;
202 char name[256]; // the same max char length with d_name in struct dirent
203 };
204
ForceRemoveDirectory(const string & path)205 bool ForceRemoveDirectory(const string& path)
206 {
207 bool ret = true;
208 int strRet;
209 DIR *dir = opendir(path.c_str());
210 if (dir == nullptr) {
211 UTILS_LOGD("Failed to open root dir: %{public}s: %{public}s ", path.c_str(), strerror(errno));
212 return false;
213 }
214 stack<DIR *> traversStack;
215 stack<DirectoryNode> removeStack;
216 traversStack.push(dir);
217 while (!traversStack.empty()) {
218 DIR *currentDir = traversStack.top();
219 traversStack.pop();
220 DirectoryNode node;
221 int currentFd = dirfd(currentDir);
222 if (currentFd < 0) {
223 UTILS_LOGD("Failed to get dirfd, fd: %{public}d: %{public}s ", currentFd, strerror(errno));
224 ret = false;
225 continue;
226 }
227
228 while (true) {
229 struct dirent *ptr = readdir(currentDir);
230 if (ptr == nullptr) {
231 break;
232 }
233 const char *name = ptr->d_name;
234 // current dir or parent dir
235 if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
236 continue;
237 }
238
239 if (ptr->d_type == DT_DIR) {
240 int subFd = openat(currentFd, name, O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
241 if (subFd < 0) {
242 UTILS_LOGD("Failed in subFd openat: %{public}s ", name);
243 ret = false;
244 continue;
245 }
246 DIR *subDir = fdopendir(subFd);
247 if (subDir == nullptr) {
248 close(subFd);
249 UTILS_LOGD("Failed in fdopendir: %{public}s", strerror(errno));
250 ret = false;
251 continue;
252 }
253 node.dir = subDir;
254 node.currentFd = currentFd;
255 strRet = strcpy_s(node.name, sizeof(node.name), name);
256 if (strRet != EOK) {
257 UTILS_LOGE("Failed to exec strcpy_s, name= %{public}s, strRet= %{public}d", name, strRet);
258 }
259 removeStack.push(node);
260 traversStack.push(subDir);
261 } else {
262 if (faccessat(currentFd, name, F_OK, AT_SYMLINK_NOFOLLOW) == 0) {
263 if (unlinkat(currentFd, name, 0) < 0) {
264 UTILS_LOGD("Couldn't unlinkat subFile %{public}s: %{public}s", name, strerror(errno));
265 ret = false;
266 break;
267 }
268 } else {
269 UTILS_LOGD("Access to file: %{public}s is failed", name);
270 ret = false;
271 break;
272 }
273 }
274 }
275 }
276 if (!ret) {
277 UTILS_LOGD("Failed to remove some subfile under path: %{public}s", path.c_str());
278 }
279 while (!removeStack.empty()) {
280 DirectoryNode node = removeStack.top();
281 removeStack.pop();
282 closedir(node.dir);
283 if (unlinkat(node.currentFd, node.name, AT_REMOVEDIR) < 0) {
284 UTILS_LOGD("Couldn't unlinkat subDir %{public}s: %{public}s", node.name, strerror(errno));
285 continue;
286 }
287 }
288 closedir(dir);
289 if (faccessat(AT_FDCWD, path.c_str(), F_OK, AT_SYMLINK_NOFOLLOW) == 0) {
290 if (remove(path.c_str()) != 0) {
291 UTILS_LOGD("Failed to remove root dir: %{public}s: %{public}s ", path.c_str(), strerror(errno));
292 return false;
293 }
294 }
295 return faccessat(AT_FDCWD, path.c_str(), F_OK, AT_SYMLINK_NOFOLLOW) != 0;
296 }
297
RemoveFile(const string & fileName)298 bool RemoveFile(const string& fileName)
299 {
300 if (access(fileName.c_str(), F_OK) == 0) {
301 return remove(fileName.c_str()) == 0;
302 }
303
304 return true;
305 }
306
IsEmptyFolder(const string & path)307 bool IsEmptyFolder(const string& path)
308 {
309 vector<string> files;
310 GetDirFiles(path, files);
311 return files.empty();
312 }
313
GetFolderSize(const string & path)314 uint64_t GetFolderSize(const string& path)
315 {
316 vector<string> files;
317 struct stat statbuf = {0};
318 GetDirFiles(path, files);
319 uint64_t totalSize = 0;
320 for (auto& file : files) {
321 if (stat(file.c_str(), &statbuf) == 0) {
322 totalSize += statbuf.st_size;
323 }
324 }
325
326 return totalSize;
327 }
328
329 // inner function, and param is legitimate
ChangeMode(const string & fileName,const mode_t & mode)330 bool ChangeMode(const string& fileName, const mode_t& mode)
331 {
332 return (chmod(fileName.c_str(), mode) == 0);
333 }
334
ChangeModeFile(const string & fileName,const mode_t & mode)335 bool ChangeModeFile(const string& fileName, const mode_t& mode)
336 {
337 if (access(fileName.c_str(), F_OK) != 0) {
338 return false;
339 }
340
341 return ChangeMode(fileName, mode);
342 }
343
ChangeModeDirectory(const string & path,const mode_t & mode)344 bool ChangeModeDirectory(const string& path, const mode_t& mode)
345 {
346 string subPath;
347 bool ret = true;
348 DIR *dir = opendir(path.c_str());
349 if (dir == nullptr) {
350 return false;
351 }
352
353 while (true) {
354 struct dirent *ptr = readdir(dir);
355 if (ptr == nullptr) {
356 break;
357 }
358
359 // current dir or parent dir
360 if (strcmp(ptr->d_name, ".") == 0 || strcmp(ptr->d_name, "..") == 0) {
361 continue;
362 }
363 subPath = IncludeTrailingPathDelimiter(path) + string(ptr->d_name);
364 if (ptr->d_type == DT_DIR) {
365 ret = ChangeModeDirectory(subPath, mode);
366 } else {
367 if (access(subPath.c_str(), F_OK) == 0) {
368 if (!ChangeMode(subPath, mode)) {
369 UTILS_LOGD("Failed to exec ChangeMode");
370 closedir(dir);
371 return false;
372 }
373 }
374 }
375 }
376 closedir(dir);
377 string currentPath = ExcludeTrailingPathDelimiter(path);
378 if (access(currentPath.c_str(), F_OK) == 0) {
379 if (!ChangeMode(currentPath, mode)) {
380 UTILS_LOGD("Failed to exec ChangeMode");
381 return false;
382 }
383 }
384 return ret;
385 }
386
PathToRealPath(const string & path,string & realPath)387 bool PathToRealPath(const string& path, string& realPath)
388 {
389 if (path.empty()) {
390 UTILS_LOGD("path is empty!");
391 return false;
392 }
393
394 if ((path.length() >= PATH_MAX)) {
395 UTILS_LOGD("path len is error, the len is: [%{public}zu]", path.length());
396 return false;
397 }
398
399 char tmpPath[PATH_MAX] = {0};
400 if (realpath(path.c_str(), tmpPath) == nullptr) {
401 UTILS_LOGD("path to realpath error");
402 return false;
403 }
404
405 realPath = tmpPath;
406 if (access(realPath.c_str(), F_OK) != 0) {
407 UTILS_LOGD("check realpath (%{private}s) error", realPath.c_str());
408 return false;
409 }
410 return true;
411 }
412
413 #if defined(IOS_PLATFORM) || defined(_WIN32)
TransformFileName(const string & fileName)414 string TransformFileName(const string& fileName)
415 {
416 string::size_type pos = fileName.find(".");
417 string transformfileName = "";
418 if (pos == string::npos) {
419 transformfileName = fileName;
420
421 #ifdef _WIN32
422 transformfileName = transformfileName.append(".dll");
423 #elif defined IOS_PLATFORM
424 transformfileName = transformfileName.append(".dylib");
425 #endif
426
427 return transformfileName;
428 } else {
429 transformfileName = string(fileName).substr(0, pos + 1);
430
431 #ifdef _WIN32
432 transformfileName = transformfileName.append("dll");
433 #elif defined IOS_PLATFORM
434 transformfileName = transformfileName.append("dylib");
435 #endif
436
437 return transformfileName;
438 }
439 }
440 #endif
441
442 } // OHOS
443