From 9f1df9ae5115672c1ecda50e036d29e95119f8af Mon Sep 17 00:00:00 2001 From: lijincheng Date: Sat, 9 Mar 2024 16:16:13 +0800 Subject: [PATCH] [Bug]: cherrypick ForceRemoveDirectory bugfix to 4.0-release Issue:https://gitee.com/openharmony/commonlibrary_c_utils/issues/I974QM Signed-off-by: lijincheng --- base/src/directory_ex.cpp | 105 ++++++++++++++---- .../unittest/common/utils_directory_test.cpp | 20 +++- 2 files changed, 100 insertions(+), 25 deletions(-) diff --git a/base/src/directory_ex.cpp b/base/src/directory_ex.cpp index 9e79b9b..77dddb7 100644 --- a/base/src/directory_ex.cpp +++ b/base/src/directory_ex.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include "securec.h" #include "unistd.h" #include "utils_log.h" @@ -195,47 +196,103 @@ bool ForceCreateDirectory(const string& path) return access(path.c_str(), F_OK) == 0; } +struct DirectoryNode { + DIR *dir; + int currentFd; + char name[256]; // the same max char length with d_name in struct dirent +}; + bool ForceRemoveDirectory(const string& path) { - string subPath; bool ret = true; + int strRet; DIR *dir = opendir(path.c_str()); if (dir == nullptr) { + UTILS_LOGD("Failed to open root dir: %{public}s: %{public}s ", path.c_str(), strerror(errno)); return false; } - - while (true) { - struct dirent *ptr = readdir(dir); - if (ptr == nullptr) { - break; - } - - // current dir or parent dir - if (strcmp(ptr->d_name, ".") == 0 || strcmp(ptr->d_name, "..") == 0) { + stack traversStack; + stack removeStack; + traversStack.push(dir); + while (!traversStack.empty()) { + DIR *currentDir = traversStack.top(); + traversStack.pop(); + DirectoryNode node; + int currentFd = dirfd(currentDir); + if (currentFd < 0) { + UTILS_LOGD("Failed to get dirfd, fd: %{public}d: %{public}s ", currentFd, strerror(errno)); + ret = false; continue; } - subPath = IncludeTrailingPathDelimiter(path) + string(ptr->d_name); - if (ptr->d_type == DT_DIR) { - ret = ForceRemoveDirectory(subPath); - } else { - if (faccessat(AT_FDCWD, subPath.c_str(), F_OK, AT_SYMLINK_NOFOLLOW) == 0) { - if (remove(subPath.c_str()) != 0) { - closedir(dir); - return false; + + while (true) { + struct dirent *ptr = readdir(currentDir); + if (ptr == nullptr) { + break; + } + const char *name = ptr->d_name; + // current dir or parent dir + if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) { + continue; + } + + if (ptr->d_type == DT_DIR) { + int subFd = openat(currentFd, name, O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC); + if (subFd < 0) { + UTILS_LOGD("Failed in subFd openat: %{public}s ", name); + ret = false; + continue; + } + DIR *subDir = fdopendir(subFd); + if (subDir == nullptr) { + close(subFd); + UTILS_LOGD("Failed in fdopendir: %{public}s", strerror(errno)); + ret = false; + continue; + } + node.dir = subDir; + node.currentFd = currentFd; + strRet = strcpy_s(node.name, sizeof(node.name), name); + if (strRet != EOK) { + UTILS_LOGE("Failed to exec strcpy_s, name= %{public}s, strRet= %{public}d", name, strRet); + } + removeStack.push(node); + traversStack.push(subDir); + } else { + if (faccessat(currentFd, name, F_OK, AT_SYMLINK_NOFOLLOW) == 0) { + if (unlinkat(currentFd, name, 0) < 0) { + UTILS_LOGD("Couldn't unlinkat subFile %{public}s: %{public}s", name, strerror(errno)); + ret = false; + break; + } + } else { + UTILS_LOGD("Access to file: %{public}s is failed", name); + ret = false; + break; } } } } + if (!ret) { + UTILS_LOGD("Failed to remove some subfile under path: %{public}s", path.c_str()); + } + while (!removeStack.empty()) { + DirectoryNode node = removeStack.top(); + removeStack.pop(); + closedir(node.dir); + if (unlinkat(node.currentFd, node.name, AT_REMOVEDIR) < 0) { + UTILS_LOGD("Couldn't unlinkat subDir %{public}s: %{public}s", node.name, strerror(errno)); + continue; + } + } closedir(dir); - - string currentPath = ExcludeTrailingPathDelimiter(path); - if (faccessat(AT_FDCWD, currentPath.c_str(), F_OK, AT_SYMLINK_NOFOLLOW) == 0) { - if (remove(currentPath.c_str()) != 0) { + if (faccessat(AT_FDCWD, path.c_str(), F_OK, AT_SYMLINK_NOFOLLOW) == 0) { + if (remove(path.c_str()) != 0) { + UTILS_LOGD("Failed to remove root dir: %{public}s: %{public}s ", path.c_str(), strerror(errno)); return false; } } - - return ret && (faccessat(AT_FDCWD, path.c_str(), F_OK, AT_SYMLINK_NOFOLLOW) != 0); + return faccessat(AT_FDCWD, path.c_str(), F_OK, AT_SYMLINK_NOFOLLOW) != 0; } bool RemoveFile(const string& fileName) diff --git a/base/test/unittest/common/utils_directory_test.cpp b/base/test/unittest/common/utils_directory_test.cpp index 3ac7148..57f7a8c 100644 --- a/base/test/unittest/common/utils_directory_test.cpp +++ b/base/test/unittest/common/utils_directory_test.cpp @@ -195,6 +195,24 @@ HWTEST_F(UtilsDirectoryTest, testForceRemoveDirectory002, TestSize.Level0) EXPECT_EQ(ret, false); } + +/* + * @tc.name: testForceRemoveDirectory003 + * @tc.desc: test whether it works when the full path is over than 255. + */ +HWTEST_F(UtilsDirectoryTest, testForceRemoveDirectory003, TestSize.Level0) +{ + string dirpath = "/data/test/tmp/tmp/tmp/tmp/tmp/tmp/tmp/tmp/tmp/tmp/tmp/tmp/tmp/tmp/" + "tmp/tmp/tmp/tmp/tmp/tmp/tmp/tmp/tmp/tmp/tmp/tmp/tmp/tmp/tmp/tmp/tmp/tmp/tmp/tmp/tmp/" + "tmp/tmp/tmp/tmp/tmp/tmp/tmp/tmp/tmp/tmp/tmp/tmp/tmp/tmp/tmp/tmp/tmp/tmp/tmp/tmp/tmp/" + "tmp/tmp/tmp/tmp/tmp/tmp/tmp/tmp/tmp/tmp/tmp/tmp/tmp/tmp/tmp/tmp/tmp/tmp/tmp/tmp/tmp"; + bool ret = ForceCreateDirectory(dirpath); + EXPECT_EQ(ret, true); + string rootpath = "/data/test/tmp"; + ret = ForceRemoveDirectory(rootpath); + EXPECT_EQ(ret, true); +} + /* * @tc.name: testRemoveFile001 * @tc.desc: directory unit test @@ -420,4 +438,4 @@ HWTEST_F(UtilsDirectoryTest, testPathToRealPath006, TestSize.Level0) EXPECT_EQ(ret, false); } } // namespace -} // namespace OHOS \ No newline at end of file +} // namespace OHOS -- Gitee