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;
120 set<string> filteredPath;
121 for (size_t i = 0; i < gl->gl_pathc; ++i) {
122 expandPath.emplace(gl->gl_pathv[i]);
123 }
124
125 for (auto it = expandPath.begin(); it != expandPath.end(); ++it) {
126 filteredPath.insert(*it);
127 if (*it->rbegin() != '/') {
128 continue;
129 }
130 auto jt = it;
131 for (++jt; jt != expandPath.end() && (jt->find(*it) == 0); ++jt) {
132 }
133
134 it = --jt;
135 }
136
137 return filteredPath;
138 }
139
GetBigFiles(const vector<string> & includes,const vector<string> & excludes)140 pair<ErrCode, map<string, struct stat>> BDir::GetBigFiles(const vector<string> &includes,
141 const vector<string> &excludes)
142 {
143 set<string> inc = ExpandPathWildcard(includes);
144
145 map<string, struct stat> incFiles;
146 for (const auto &item : inc) {
147 auto [errCode, files] =
148 OHOS::FileManagement::Backup::GetDirFilesDetail(item, true, BConstants::BIG_FILE_BOUNDARY);
149 if (errCode == 0) {
150 int32_t num = static_cast<int32_t>(files.size());
151 HILOGI("found big files. total number is : %{public}d", num);
152 incFiles.merge(move(files));
153 }
154 }
155
156 auto isMatch = [](const vector<string> &s, const string &str) -> bool {
157 if (str.empty()) {
158 return false;
159 }
160 for (const string &item : s) {
161 if (!item.empty() && (fnmatch(item.data(), str.data(), FNM_LEADING_DIR) == 0)) {
162 HILOGI("file %{public}s matchs exclude condition", str.c_str());
163 return true;
164 }
165 }
166 return false;
167 };
168
169 map<string, struct stat> bigFiles;
170 for (const auto &item : incFiles) {
171 if (!isMatch(excludes, item.first)) {
172 HILOGI("file %{public}s matchs include condition and unmatchs exclude condition", item.first.c_str());
173 bigFiles[item.first] = item.second;
174 }
175 }
176 int32_t num = static_cast<int32_t>(bigFiles.size());
177 HILOGI("total number of big files is %{public}d", num);
178 return {ERR_OK, move(bigFiles)};
179 }
180
GetDirs(const vector<string_view> & paths)181 vector<string> BDir::GetDirs(const vector<string_view> &paths)
182 {
183 vector<string> wildcardPath(paths.begin(), paths.end());
184 set<string> inc = ExpandPathWildcard(wildcardPath);
185 vector<string> dirs(inc.begin(), inc.end());
186 return dirs;
187 }
188 } // namespace OHOS::FileManagement::Backup