1 /*
2 * Copyright (c) 2022-2023 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 "b_filesystem/b_dir.h"
17
18 #include <algorithm>
19 #include <dirent.h>
20 #include <fnmatch.h>
21 #include <functional>
22 #include <glob.h>
23 #include <memory>
24 #include <set>
25 #include <string>
26 #include <tuple>
27 #include <vector>
28
29 #include "b_error/b_error.h"
30 #include "b_resources/b_constants.h"
31 #include "directory_ex.h"
32 #include "errors.h"
33 #include "filemgmt_libhilog.h"
34
35 namespace OHOS::FileManagement::Backup {
36 using namespace std;
37
GetDirFilesDetail(const string & path,bool recursion,off_t size=-1)38 pair<ErrCode, map<string, struct stat>> GetDirFilesDetail(const string &path, bool recursion, off_t size = -1)
39 {
40 map<string, struct stat> files;
41 unique_ptr<DIR, function<void(DIR *)>> dir = {opendir(path.c_str()), closedir};
42 if (!dir) {
43 HILOGE("Invalid directory path: %{private}s", path.c_str());
44 return {BError(errno).GetCode(), files};
45 }
46
47 struct dirent *ptr = nullptr;
48 while (!!(ptr = readdir(dir.get()))) {
49 // current dir OR parent dir
50 if ((strcmp(ptr->d_name, ".") == 0) || (strcmp(ptr->d_name, "..") == 0)) {
51 continue;
52 } else if (ptr->d_type == DT_DIR) {
53 if (!recursion) {
54 continue;
55 }
56
57 auto [errCode, subfiles] =
58 GetDirFilesDetail(IncludeTrailingPathDelimiter(path) + string(ptr->d_name), recursion, size);
59 if (errCode != 0) {
60 return {errCode, files};
61 }
62 files.merge(subfiles);
63 } else if (ptr->d_type == DT_LNK) {
64 continue;
65 } else {
66 struct stat sta = {};
67 string fileName = IncludeTrailingPathDelimiter(path) + string(ptr->d_name);
68 if (stat(fileName.data(), &sta) == -1) {
69 continue;
70 }
71 if (sta.st_size < size) {
72 continue;
73 }
74 HILOGI("Find big file");
75 files.try_emplace(fileName, sta);
76 }
77 }
78
79 return {BError(BError::Codes::OK).GetCode(), files};
80 }
81
GetDirFiles(const string & path)82 tuple<ErrCode, vector<string>> BDir::GetDirFiles(const string &path)
83 {
84 vector<string> files;
85 unique_ptr<DIR, function<void(DIR *)>> dir = {opendir(path.c_str()), closedir};
86 if (!dir) {
87 HILOGE("Invalid directory path: %{private}s", path.c_str());
88 return {BError(errno).GetCode(), files};
89 }
90
91 struct dirent *ptr = nullptr;
92 while (!!(ptr = readdir(dir.get()))) {
93 // current dir OR parent dir
94 if ((strcmp(ptr->d_name, ".") == 0) || (strcmp(ptr->d_name, "..") == 0)) {
95 continue;
96 } else if (ptr->d_type == DT_DIR) {
97 continue;
98 } else {
99 files.push_back(IncludeTrailingPathDelimiter(path) + string(ptr->d_name));
100 }
101 }
102
103 return {BError(BError::Codes::OK).GetCode(), files};
104 }
105
ExpandPathWildcard(const vector<string> & vec)106 static set<string> ExpandPathWildcard(const vector<string> &vec)
107 {
108 unique_ptr<glob_t, function<void(glob_t *)>> gl {new glob_t, [](glob_t *ptr) { globfree(ptr); }};
109 *gl = {};
110
111 int flags = GLOB_DOOFFS | GLOB_MARK;
112 for (const string &pattern : vec) {
113 if (!pattern.empty()) {
114 glob(pattern.data(), flags, NULL, gl.get());
115 flags |= GLOB_APPEND;
116 }
117 }
118
119 set<string> expandPath, filteredPath;
120 for (size_t i = 0; i < gl->gl_pathc; ++i) {
121 expandPath.emplace(gl->gl_pathv[i]);
122 }
123
124 for (auto it = expandPath.begin(); it != expandPath.end(); ++it) {
125 filteredPath.insert(*it);
126 if (*it->rbegin() != '/') {
127 continue;
128 }
129 auto jt = it;
130 for (++jt; jt != expandPath.end() && (jt->find(*it) == 0); ++jt) {
131 }
132
133 it = --jt;
134 }
135
136 return filteredPath;
137 }
138
GetBigFiles(const vector<string> & includes,const vector<string> & excludes)139 pair<ErrCode, map<string, struct stat>> BDir::GetBigFiles(const vector<string> &includes,
140 const vector<string> &excludes)
141 {
142 set<string> inc = ExpandPathWildcard(includes);
143
144 map<string, struct stat> incFiles;
145 for (const auto &item : inc) {
146 auto [errCode, files] =
147 OHOS::FileManagement::Backup::GetDirFilesDetail(item, true, BConstants::BIG_FILE_BOUNDARY);
148 if (errCode == 0) {
149 int32_t num = static_cast<int32_t>(files.size());
150 HILOGI("found big files. total number is : %{public}d", num);
151 incFiles.merge(move(files));
152 }
153 }
154
155 auto isMatch = [](const vector<string> &s, const string &str) -> bool {
156 if (str.empty()) {
157 return false;
158 }
159 for (const string &item : s) {
160 if (!item.empty() && (fnmatch(item.data(), str.data(), FNM_LEADING_DIR) == 0)) {
161 HILOGI("file %{public}s matchs exclude condition", str.c_str());
162 return true;
163 }
164 }
165 return false;
166 };
167
168 map<string, struct stat> bigFiles;
169 for (const auto &item : incFiles) {
170 if (!isMatch(excludes, item.first)) {
171 HILOGI("file %{public}s matchs include condition and unmatchs exclude condition", item.first.c_str());
172 bigFiles[item.first] = item.second;
173 }
174 }
175 int32_t num = static_cast<int32_t>(bigFiles.size());
176 HILOGI("total number of big files is %{public}d", num);
177 return {ERR_OK, move(bigFiles)};
178 }
179
GetDirs(const vector<string_view> & paths)180 vector<string> BDir::GetDirs(const vector<string_view> &paths)
181 {
182 vector<string> wildcardPath(paths.begin(), paths.end());
183 set<string> inc = ExpandPathWildcard(wildcardPath);
184 vector<string> dirs(inc.begin(), inc.end());
185 return dirs;
186 }
187 } // namespace OHOS::FileManagement::Backup