• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
IsEmptyDirectory(const string & path)38 static bool IsEmptyDirectory(const string &path)
39 {
40     DIR *dir = opendir(path.c_str());
41     if (dir == nullptr) {
42         return false;
43     }
44     bool isEmpty = true;
45     struct dirent *entry;
46     while ((entry = readdir(dir)) != nullptr) {
47         if (entry->d_type != DT_DIR || (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0)) {
48             isEmpty = false;
49             break;
50         }
51     }
52     closedir(dir);
53     return isEmpty;
54 }
55 
GetFile(const string & path,off_t size=-1)56 static tuple<ErrCode, map<string, struct stat>, vector<string>> GetFile(const string &path, off_t size = -1)
57 {
58     map<string, struct stat> files;
59     vector<string> smallFiles;
60     struct stat sta = {};
61     if (stat(path.data(), &sta) == -1) {
62         return {BError(errno).GetCode(), files, smallFiles};
63     }
64     if (path == "/") {
65         return {BError(BError::Codes::OK).GetCode(), files, smallFiles};
66     }
67     if (sta.st_size <= size) {
68         smallFiles.emplace_back(path);
69     } else {
70         files.try_emplace(path, sta);
71     }
72     return {BError(BError::Codes::OK).GetCode(), files, smallFiles};
73 }
74 
GetDirFilesDetail(const string & path,bool recursion,off_t size=-1)75 static tuple<ErrCode, map<string, struct stat>, vector<string>> GetDirFilesDetail(const string &path,
76                                                                                   bool recursion,
77                                                                                   off_t size = -1)
78 {
79     map<string, struct stat> files;
80     vector<string> smallFiles;
81 
82     if (IsEmptyDirectory(path)) {
83         string newPath = path;
84         if (path.at(path.size()-1) != '/') {
85             newPath += '/';
86         }
87         smallFiles.emplace_back(newPath);
88         return {ERR_OK, files, smallFiles};
89     }
90 
91     unique_ptr<DIR, function<void(DIR *)>> dir = {opendir(path.c_str()), closedir};
92     if (!dir) {
93         HILOGE("Invalid directory path: %{private}s", path.c_str());
94         return GetFile(path, size);
95     }
96     struct dirent *ptr = nullptr;
97     while (!!(ptr = readdir(dir.get()))) {
98         // current dir OR parent dir
99         if ((strcmp(ptr->d_name, ".") == 0) || (strcmp(ptr->d_name, "..") == 0)) {
100             continue;
101         } else if (ptr->d_type == DT_DIR) {
102             if (!recursion) {
103                 continue;
104             }
105             auto [errCode, subFiles, subSmallFiles] =
106                 GetDirFilesDetail(IncludeTrailingPathDelimiter(path) + string(ptr->d_name), recursion, size);
107             if (errCode != 0) {
108                 return {errCode, files, smallFiles};
109             }
110             files.merge(subFiles);
111             smallFiles.insert(smallFiles.end(), subSmallFiles.begin(), subSmallFiles.end());
112         } else if (ptr->d_type == DT_LNK) {
113             continue;
114         } else {
115             struct stat sta = {};
116             string fileName = IncludeTrailingPathDelimiter(path) + string(ptr->d_name);
117             if (stat(fileName.data(), &sta) == -1) {
118                 continue;
119             }
120             if (sta.st_size <= size) {
121                 smallFiles.emplace_back(fileName);
122                 continue;
123             }
124 
125             files.try_emplace(fileName, sta);
126         }
127     }
128     return {ERR_OK, files, smallFiles};
129 }
130 
GetDirFiles(const string & path)131 tuple<ErrCode, vector<string>> BDir::GetDirFiles(const string &path)
132 {
133     vector<string> files;
134     unique_ptr<DIR, function<void(DIR *)>> dir = {opendir(path.c_str()), closedir};
135     if (!dir) {
136         HILOGE("Invalid directory path: %{private}s", path.c_str());
137         return {BError(errno).GetCode(), files};
138     }
139 
140     struct dirent *ptr = nullptr;
141     while (!!(ptr = readdir(dir.get()))) {
142         // current dir OR parent dir
143         if ((strcmp(ptr->d_name, ".") == 0) || (strcmp(ptr->d_name, "..") == 0)) {
144             continue;
145         } else if (ptr->d_type == DT_DIR) {
146             continue;
147         } else {
148             files.push_back(IncludeTrailingPathDelimiter(path) + string(ptr->d_name));
149         }
150     }
151 
152     return {ERR_OK, files};
153 }
154 
ExpandPathWildcard(const vector<string> & vec,bool onlyPath)155 static set<string> ExpandPathWildcard(const vector<string> &vec, bool onlyPath)
156 {
157     unique_ptr<glob_t, function<void(glob_t *)>> gl {new glob_t, [](glob_t *ptr) { globfree(ptr); }};
158     *gl = {};
159 
160     int flags = GLOB_DOOFFS | GLOB_MARK;
161     for (const string &pattern : vec) {
162         if (!pattern.empty()) {
163             glob(pattern.data(), flags, NULL, gl.get());
164             flags |= GLOB_APPEND;
165         }
166     }
167 
168     set<string> expandPath;
169     set<string> filteredPath;
170     for (size_t i = 0; i < gl->gl_pathc; ++i) {
171         expandPath.emplace(gl->gl_pathv[i]);
172     }
173 
174     for (auto it = expandPath.begin(); it != expandPath.end(); ++it) {
175         filteredPath.insert(*it);
176         if (onlyPath && *it->rbegin() != '/') {
177             continue;
178         }
179         auto jt = it;
180         for (++jt; jt != expandPath.end() && (jt->find(*it) == 0); ++jt) {
181         }
182 
183         it = --jt;
184     }
185 
186     return filteredPath;
187 }
188 
GetBigFiles(const vector<string> & includes,const vector<string> & excludes)189 tuple<ErrCode, map<string, struct stat>, vector<string>> BDir::GetBigFiles(const vector<string> &includes,
190                                                                            const vector<string> &excludes)
191 {
192     set<string> inc = ExpandPathWildcard(includes, false);
193 
194     map<string, struct stat> incFiles;
195     vector<string> incSmallFiles;
196     for (const auto &item : inc) {
197         auto [errCode, files, smallFiles] = GetDirFilesDetail(item, true, BConstants::BIG_FILE_BOUNDARY);
198         if (errCode == 0) {
199             int32_t num = static_cast<int32_t>(files.size());
200             incFiles.merge(move(files));
201             HILOGI("big files: %{public}d; small files: %{public}d", num, static_cast<int32_t>(smallFiles.size()));
202             incSmallFiles.insert(incSmallFiles.end(), smallFiles.begin(), smallFiles.end());
203         }
204     }
205 
206     auto isMatch = [](const vector<string> &s, const string &str) -> bool {
207         if (str.empty()) {
208             return false;
209         }
210         for (const string &item : s) {
211             if (item.empty()) {
212                 continue;
213             }
214             string excludeItem = item;
215             if (excludeItem.at(item.size() - 1) == '/') {
216                 excludeItem += "*";
217             }
218             if (fnmatch(excludeItem.data(), str.data(), FNM_LEADING_DIR) == 0) {
219                 return true;
220             }
221         }
222         return false;
223     };
224 
225     vector<string> resSmallFiles;
226     for (const auto &item : incSmallFiles) {
227         if (!isMatch(excludes, item)) {
228             resSmallFiles.emplace_back(item);
229         }
230     }
231 
232     map<string, struct stat> bigFiles;
233     for (const auto &item : incFiles) {
234         if (!isMatch(excludes, item.first)) {
235             bigFiles[item.first] = item.second;
236         }
237     }
238     HILOGI("total number of big files is %{public}d", static_cast<int32_t>(bigFiles.size()));
239     HILOGI("total number of small files is %{public}d", static_cast<int32_t>(resSmallFiles.size()));
240     return {ERR_OK, move(bigFiles), move(resSmallFiles)};
241 }
242 
GetDirs(const vector<string_view> & paths)243 vector<string> BDir::GetDirs(const vector<string_view> &paths)
244 {
245     vector<string> wildcardPath(paths.begin(), paths.end());
246     set<string> inc = ExpandPathWildcard(wildcardPath, true);
247     vector<string> dirs(inc.begin(), inc.end());
248     return dirs;
249 }
250 
251 } // namespace OHOS::FileManagement::Backup