• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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