/* * Copyright (c) 2022-2023 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "b_filesystem/b_dir.h" #include #include #include #include #include #include #include #include #include #include #include "b_error/b_error.h" #include "b_resources/b_constants.h" #include "directory_ex.h" #include "errors.h" #include "filemgmt_libhilog.h" namespace OHOS::FileManagement::Backup { using namespace std; static bool IsEmptyDirectory(const string &path) { DIR *dir = opendir(path.c_str()); if (dir == nullptr) { return false; } bool isEmpty = true; struct dirent *entry; while ((entry = readdir(dir)) != nullptr) { if (entry->d_type != DT_DIR || (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0)) { isEmpty = false; break; } } closedir(dir); return isEmpty; } static tuple, vector> GetFile(const string &path, off_t size = -1) { map files; vector smallFiles; struct stat sta = {}; if (stat(path.data(), &sta) == -1) { return {BError(errno).GetCode(), files, smallFiles}; } if (path == "/") { return {BError(BError::Codes::OK).GetCode(), files, smallFiles}; } if (sta.st_size <= size) { smallFiles.emplace_back(path); } else { files.try_emplace(path, sta); } return {BError(BError::Codes::OK).GetCode(), files, smallFiles}; } static tuple, vector> GetDirFilesDetail(const string &path, bool recursion, off_t size = -1) { map files; vector smallFiles; if (IsEmptyDirectory(path)) { string newPath = path; if (path.at(path.size()-1) != '/') { newPath += '/'; } smallFiles.emplace_back(newPath); return {ERR_OK, files, smallFiles}; } unique_ptr> dir = {opendir(path.c_str()), closedir}; if (!dir) { HILOGE("Invalid directory path: %{private}s", path.c_str()); return GetFile(path, size); } struct dirent *ptr = nullptr; while (!!(ptr = readdir(dir.get()))) { // current dir OR parent dir if ((strcmp(ptr->d_name, ".") == 0) || (strcmp(ptr->d_name, "..") == 0)) { continue; } else if (ptr->d_type == DT_DIR) { if (!recursion) { continue; } auto [errCode, subFiles, subSmallFiles] = GetDirFilesDetail(IncludeTrailingPathDelimiter(path) + string(ptr->d_name), recursion, size); if (errCode != 0) { return {errCode, files, smallFiles}; } files.merge(subFiles); smallFiles.insert(smallFiles.end(), subSmallFiles.begin(), subSmallFiles.end()); } else if (ptr->d_type == DT_LNK) { continue; } else { struct stat sta = {}; string fileName = IncludeTrailingPathDelimiter(path) + string(ptr->d_name); if (stat(fileName.data(), &sta) == -1) { continue; } if (sta.st_size <= size) { smallFiles.emplace_back(fileName); continue; } files.try_emplace(fileName, sta); } } return {ERR_OK, files, smallFiles}; } tuple> BDir::GetDirFiles(const string &path) { vector files; unique_ptr> dir = {opendir(path.c_str()), closedir}; if (!dir) { HILOGE("Invalid directory path: %{private}s", path.c_str()); return {BError(errno).GetCode(), files}; } struct dirent *ptr = nullptr; while (!!(ptr = readdir(dir.get()))) { // current dir OR parent dir if ((strcmp(ptr->d_name, ".") == 0) || (strcmp(ptr->d_name, "..") == 0)) { continue; } else if (ptr->d_type == DT_DIR) { continue; } else { files.push_back(IncludeTrailingPathDelimiter(path) + string(ptr->d_name)); } } return {ERR_OK, files}; } static set ExpandPathWildcard(const vector &vec, bool onlyPath) { unique_ptr> gl {new glob_t, [](glob_t *ptr) { globfree(ptr); }}; *gl = {}; int flags = GLOB_DOOFFS | GLOB_MARK; for (const string &pattern : vec) { if (!pattern.empty()) { glob(pattern.data(), flags, NULL, gl.get()); flags |= GLOB_APPEND; } } set expandPath; set filteredPath; for (size_t i = 0; i < gl->gl_pathc; ++i) { expandPath.emplace(gl->gl_pathv[i]); } for (auto it = expandPath.begin(); it != expandPath.end(); ++it) { filteredPath.insert(*it); if (onlyPath && *it->rbegin() != '/') { continue; } auto jt = it; for (++jt; jt != expandPath.end() && (jt->find(*it) == 0); ++jt) { } it = --jt; } return filteredPath; } tuple, vector> BDir::GetBigFiles(const vector &includes, const vector &excludes) { set inc = ExpandPathWildcard(includes, false); map incFiles; vector incSmallFiles; for (const auto &item : inc) { auto [errCode, files, smallFiles] = GetDirFilesDetail(item, true, BConstants::BIG_FILE_BOUNDARY); if (errCode == 0) { int32_t num = static_cast(files.size()); incFiles.merge(move(files)); HILOGI("big files: %{public}d; small files: %{public}d", num, static_cast(smallFiles.size())); incSmallFiles.insert(incSmallFiles.end(), smallFiles.begin(), smallFiles.end()); } } auto isMatch = [](const vector &s, const string &str) -> bool { if (str.empty()) { return false; } for (const string &item : s) { if (item.empty()) { continue; } string excludeItem = item; if (excludeItem.at(item.size() - 1) == '/') { excludeItem += "*"; } if (fnmatch(excludeItem.data(), str.data(), FNM_LEADING_DIR) == 0) { return true; } } return false; }; vector resSmallFiles; for (const auto &item : incSmallFiles) { if (!isMatch(excludes, item)) { resSmallFiles.emplace_back(item); } } map bigFiles; for (const auto &item : incFiles) { if (!isMatch(excludes, item.first)) { bigFiles[item.first] = item.second; } } HILOGI("total number of big files is %{public}d", static_cast(bigFiles.size())); HILOGI("total number of small files is %{public}d", static_cast(resSmallFiles.size())); return {ERR_OK, move(bigFiles), move(resSmallFiles)}; } vector BDir::GetDirs(const vector &paths) { vector wildcardPath(paths.begin(), paths.end()); set inc = ExpandPathWildcard(wildcardPath, true); vector dirs(inc.begin(), inc.end()); return dirs; } } // namespace OHOS::FileManagement::Backup