1 /*
2 * Copyright (c) 2025 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 "listfile_core.h"
17
18 #include <fnmatch.h>
19 #include <memory>
20 #include <string>
21 #include <string_view>
22 #include <thread>
23 #include <tuple>
24 #include <sys/stat.h>
25
26 #include "file_utils.h"
27 #include "filemgmt_libhilog.h"
28
29 namespace OHOS::FileManagement::ModuleFileIO {
30 using namespace std;
31
32 thread_local OptionArgs g_optionArgsCore;
33
CheckSuffix(const vector<string> & suffixs)34 static bool CheckSuffix(const vector<string> &suffixs)
35 {
36 for (string suffix : suffixs) {
37 if (suffix.length() <= 1 || suffix.length() > MAX_SUFFIX_LENGTH) {
38 return false;
39 }
40 if (suffix[0] != '.') {
41 return false;
42 }
43 for (size_t i = 1; i < suffix.length(); i++) {
44 if (!isalnum(suffix[i])) {
45 return false;
46 }
47 }
48 }
49 return true;
50 }
51
ValidFileFilterParam(FsFileFilter & fsFilter,FileFilter * filter)52 static bool ValidFileFilterParam(FsFileFilter &fsFilter, FileFilter *filter)
53 {
54 auto suffixs = fsFilter.GetSuffix();
55 if (fsFilter.GetSuffix().has_value()) {
56 vector<string> suffixs = fsFilter.GetSuffix().value();
57 if (!CheckSuffix(suffixs) || suffixs.size() == 0) {
58 HILOGE("Invalid suffix.");
59 return false;
60 }
61 filter->SetSuffix(suffixs);
62 }
63
64 if (fsFilter.GetDisplayName().has_value()) {
65 vector<string> displayNames = fsFilter.GetDisplayName().value();
66 if (displayNames.size() == 0) {
67 HILOGE("Invalid displayName.");
68 return false;
69 }
70 filter->SetDisplayName(displayNames);
71 }
72
73 if (fsFilter.GetFileSizeOver().has_value()) {
74 int64_t fileSizeOver = fsFilter.GetFileSizeOver().value();
75 if (fileSizeOver < 0) {
76 HILOGE("Failed to get fileSizeOver prop.");
77 return false;
78 }
79 filter->SetFileSizeOver(fileSizeOver);
80 }
81
82 if (fsFilter.GetLastModifiedAfter().has_value()) {
83 double lastModifiedAfter = fsFilter.GetLastModifiedAfter().value();
84 if (lastModifiedAfter < 0) {
85 HILOGE("Failed to get lastModifiedAfter prop.");
86 return false;
87 }
88 filter->SetLastModifiedAfter(lastModifiedAfter);
89 }
90
91 return true;
92 }
93
ValidOptionParam(const string & path,const optional<FsListFileOptions> & opt,OptionArgs & optionArgs)94 static bool ValidOptionParam(const string &path, const optional<FsListFileOptions> &opt, OptionArgs &optionArgs)
95 {
96 g_optionArgsCore.Clear();
97 g_optionArgsCore.path = path;
98
99 if (opt.has_value()) {
100 auto op = opt.value();
101 if (op.listNum < 0) {
102 HILOGE("Failed to get listNum prop");
103 return false;
104 }
105
106 optionArgs.recursion = op.recursion;
107 optionArgs.listNum = op.listNum;
108
109 if (op.filter.has_value()) {
110 bool ret = ValidFileFilterParam(op.filter.value(), &(optionArgs.filter));
111 if (!ret) {
112 HILOGE("Failed to get filter prop.");
113 return false;
114 }
115 }
116 }
117
118 return true;
119 }
120
FilterSuffix(const vector<string> & suffixs,const struct dirent & filename)121 static bool FilterSuffix(const vector<string> &suffixs, const struct dirent &filename)
122 {
123 if (filename.d_type == DT_DIR) {
124 return true;
125 }
126 size_t found = string(filename.d_name).rfind('.');
127 if (found == std::string::npos) {
128 return false;
129 }
130 string suffixStr = string(filename.d_name).substr(found);
131 for (const auto &iter : suffixs) {
132 if (iter == suffixStr) {
133 return true;
134 }
135 }
136 return false;
137 }
138
FilterDisplayname(const vector<string> & displaynames,const struct dirent & filename)139 static bool FilterDisplayname(const vector<string> &displaynames, const struct dirent &filename)
140 {
141 for (const auto &iter : displaynames) {
142 int ret = fnmatch(iter.c_str(), filename.d_name, FNM_PATHNAME | FNM_PERIOD);
143 if (ret == 0) {
144 return true;
145 }
146 }
147 return false;
148 }
149
FilterFilesizeOver(const int64_t fFileSizeOver,const struct dirent & filename)150 static bool FilterFilesizeOver(const int64_t fFileSizeOver, const struct dirent &filename)
151 {
152 if (fFileSizeOver < 0) {
153 return true;
154 }
155 struct stat info;
156 string stPath = (g_optionArgsCore.path + '/' + string(filename.d_name));
157 int32_t res = stat(stPath.c_str(), &info);
158 if (res != 0) {
159 HILOGE("Failed to stat file.");
160 return false;
161 }
162 if (info.st_size > fFileSizeOver) {
163 return true;
164 }
165 return false;
166 }
167
FilterLastModifyTime(const double lastModifiedAfter,const struct dirent & filename)168 static bool FilterLastModifyTime(const double lastModifiedAfter, const struct dirent &filename)
169 {
170 if (lastModifiedAfter < 0) {
171 return true;
172 }
173 struct stat info;
174 string stPath = g_optionArgsCore.path + '/' + string(filename.d_name);
175 int32_t res = stat(stPath.c_str(), &info);
176 if (res != 0) {
177 HILOGE("Failed to stat file.");
178 return false;
179 }
180 if (static_cast<double>(info.st_mtime) > lastModifiedAfter) {
181 return true;
182 }
183 return false;
184 }
185
FilterResult(const struct dirent & filename)186 static bool FilterResult(const struct dirent &filename)
187 {
188 vector<string> fSuffixs = g_optionArgsCore.filter.GetSuffix();
189 if (!FilterSuffix(fSuffixs, filename) && fSuffixs.size() > 0) {
190 return false;
191 }
192 vector<string> fDisplaynames = g_optionArgsCore.filter.GetDisplayName();
193 if (!FilterDisplayname(fDisplaynames, filename) && fDisplaynames.size() > 0) {
194 return false;
195 }
196 int64_t fFileSizeOver = g_optionArgsCore.filter.GetFileSizeOver();
197 if (!FilterFilesizeOver(fFileSizeOver, filename)) {
198 return false;
199 }
200 double fLastModifiedAfter = g_optionArgsCore.filter.GetLastModifiedAfter();
201 if (!FilterLastModifyTime(fLastModifiedAfter, filename)) {
202 return false;
203 }
204 g_optionArgsCore.countNum++;
205 return true;
206 }
207
FilterFunc(const struct dirent * filename)208 static int32_t FilterFunc(const struct dirent *filename)
209 {
210 if (string_view(filename->d_name) == "." || string_view(filename->d_name) == "..") {
211 return FILTER_DISMATCH;
212 }
213
214 if (g_optionArgsCore.countNum < g_optionArgsCore.listNum || g_optionArgsCore.listNum == 0) {
215 if ((filename->d_type == DT_DIR && g_optionArgsCore.recursion) || FilterResult(*filename)) {
216 return FILTER_MATCH;
217 }
218 }
219 return FILTER_DISMATCH;
220 }
221
Deleter(struct NameListArg * arg)222 static void Deleter(struct NameListArg *arg)
223 {
224 for (int i = 0; i < arg->direntNum; i++) {
225 free((arg->namelist)[i]);
226 (arg->namelist)[i] = nullptr;
227 }
228 free(arg->namelist);
229 arg->namelist = nullptr;
230 delete arg;
231 arg = nullptr;
232 }
233
FilterFileRes(const string & path,vector<string> & dirents)234 static int FilterFileRes(const string &path, vector<string> &dirents)
235 {
236 unique_ptr<struct NameListArg, decltype(Deleter) *> pNameList = { new (nothrow) struct NameListArg, Deleter };
237 if (!pNameList) {
238 HILOGE("Failed to request heap memory.");
239 return ENOMEM;
240 }
241 int num = scandir(path.c_str(), &(pNameList->namelist), FilterFunc, nullptr);
242 if (num < 0) {
243 HILOGE("Failed to scan dir");
244 return errno;
245 }
246 pNameList->direntNum = num;
247 for (int i = 0; i < num; i++) {
248 dirents.emplace_back(pNameList->namelist[i]->d_name);
249 }
250 return ERRNO_NOERR;
251 }
252
RecursiveFunc(const string & path,vector<string> & dirents)253 static int RecursiveFunc(const string &path, vector<string> &dirents)
254 {
255 unique_ptr<struct NameListArg, decltype(Deleter) *> pNameList = { new (nothrow) struct NameListArg, Deleter };
256 if (!pNameList) {
257 HILOGE("Failed to request heap memory.");
258 return ENOMEM;
259 }
260 int num = scandir(path.c_str(), &(pNameList->namelist), FilterFunc, nullptr);
261 if (num < 0) {
262 HILOGE("Failed to scan dir");
263 return errno;
264 }
265 pNameList->direntNum = num;
266 for (int i = 0; i < num; i++) {
267 if ((*(pNameList->namelist[i])).d_type == DT_REG) {
268 dirents.emplace_back(path + '/' + pNameList->namelist[i]->d_name);
269 } else if ((*(pNameList->namelist[i])).d_type == DT_DIR) {
270 string pathTemp = g_optionArgsCore.path;
271 g_optionArgsCore.path += '/' + string((*(pNameList->namelist[i])).d_name);
272 int ret = RecursiveFunc(g_optionArgsCore.path, dirents);
273 if (ret != ERRNO_NOERR) {
274 return ret;
275 }
276 g_optionArgsCore.path = pathTemp;
277 }
278 }
279 return ERRNO_NOERR;
280 }
281
DoListFileVector(const string & path,vector<string> & dirents,bool recursion)282 static void DoListFileVector(const string &path, vector<string> &dirents, bool recursion)
283 {
284 if (recursion) {
285 for (size_t i = 0; i < dirents.size(); i++) {
286 dirents[i] = dirents[i].substr(path.length());
287 }
288 }
289 }
290
DoListFile(const string & path,const optional<FsListFileOptions> & opt)291 FsResult<std::vector<std::string>> ListFileCore::DoListFile(const string &path, const optional<FsListFileOptions> &opt)
292 {
293 if (!ValidOptionParam(path, opt, g_optionArgsCore)) {
294 HILOGE("Invalid options");
295 return FsResult<std::vector<std::string>>::Error(EINVAL);
296 }
297
298 vector<string> direntsRes;
299 int ret = 0;
300 ret = g_optionArgsCore.recursion ? RecursiveFunc(path, direntsRes) : FilterFileRes(path, direntsRes);
301 if (ret) {
302 return FsResult<std::vector<std::string>>::Error(ret);
303 }
304 DoListFileVector(path, direntsRes, g_optionArgsCore.recursion);
305 g_optionArgsCore.Clear();
306
307 return FsResult<std::vector<std::string>>::Success(direntsRes);
308 }
309
310 } // namespace OHOS::FileManagement::ModuleFileIO