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" 24using namespace std; 25 26namespace OHOS { 27 28#ifdef UTILS_CXX_RUST 29rust::String RustGetCurrentProcFullFileName() 30{ 31 return rust::String(GetCurrentProcFullFileName()); 32} 33 34rust::String RustGetCurrentProcPath() 35{ 36 return rust::String(GetCurrentProcPath()); 37} 38 39rust::String RustExtractFilePath(const rust::String& fileFullName) 40{ 41 std::string tmpName = std::string(fileFullName); 42 return rust::String(ExtractFilePath(tmpName)); 43} 44 45rust::String RustExtractFileName(const rust::String& fileFullName) 46{ 47 std::string tmpName = std::string(fileFullName); 48 return rust::String(ExtractFileName(tmpName)); 49} 50 51rust::String RustExtractFileExt(const rust::String& fileName) 52{ 53 std::string tmpName = std::string(fileName); 54 return rust::String(ExtractFileExt(tmpName)); 55} 56 57rust::String RustExcludeTrailingPathDelimiter(const rust::String& path) 58{ 59 std::string tmpPath = std::string(path); 60 return rust::String(ExcludeTrailingPathDelimiter(tmpPath)); 61} 62 63rust::String RustIncludeTrailingPathDelimiter(const rust::String& path) 64{ 65 std::string tmpPath = std::string(path); 66 return rust::String(IncludeTrailingPathDelimiter(tmpPath)); 67} 68 69bool 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 82void 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 91string 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 103string GetCurrentProcPath() 104{ 105 return ExtractFilePath(GetCurrentProcFullFileName()); 106} 107 108string ExtractFilePath(const string& fileFullName) 109{ 110 return string(fileFullName).substr(0, fileFullName.rfind("/") + 1); 111} 112 113std::string ExtractFileName(const std::string& fileFullName) 114{ 115 return string(fileFullName).substr(fileFullName.rfind("/") + 1, fileFullName.size()); 116} 117 118string 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 128string 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 141string 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 153void 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 206bool 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 228struct DirectoryNode { 229 DIR *dir; 230 int currentFd; 231 char name[256]; // the same max char length with d_name in struct dirent 232}; 233 234bool 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 327bool 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 336bool IsEmptyFolder(const string& path) 337{ 338 vector<string> files; 339 GetDirFiles(path, files); 340 return files.empty(); 341} 342 343uint64_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 359bool ChangeMode(const string& fileName, const mode_t& mode) 360{ 361 return (chmod(fileName.c_str(), mode) == 0); 362} 363 364bool 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 373bool 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 416bool 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) 443string 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