• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 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 "listfile.h"
17 
18 #include <fnmatch.h>
19 #include <memory>
20 #include <securec.h>
21 #include <string>
22 #include <string_view>
23 #include <sys/stat.h>
24 #include <thread>
25 #include <tuple>
26 
27 #include "filemgmt_libhilog.h"
28 
29 namespace OHOS::FileManagement::ModuleFileIO {
30 using namespace std;
31 using namespace OHOS::FileManagement::LibN;
32 
33 thread_local OptionArgs g_optionArgs;
34 
CheckSuffix(const vector<string> & suffixs)35 static bool CheckSuffix(const vector<string> &suffixs)
36 {
37     for (string suffix : suffixs) {
38         if (suffix.length() <= 1 || suffix.length() > MAX_SUFFIX_LENGTH) {
39             return false;
40         }
41         if (suffix[0] != '.') {
42             return false;
43         }
44         for (size_t i = 1; i < suffix.length(); i++) {
45             if (!isalnum(suffix[i])) {
46                 return false;
47             }
48         }
49     }
50     return true;
51 }
52 
GetFileFilterParam(const NVal & argv,OHOS::DistributedFS::FileFilter * filter)53 static bool GetFileFilterParam(const NVal &argv, OHOS::DistributedFS::FileFilter *filter)
54 {
55     bool ret = false;
56     if (argv.HasProp("suffix")) {
57         vector<string> suffixs;
58         tie(ret, suffixs, ignore) = argv.GetProp("suffix").ToStringArray();
59         if (!ret) {
60             HILOGE("Failed to get suffix prop.");
61             return false;
62         }
63         if (!CheckSuffix(suffixs) || suffixs.size() == 0) {
64             HILOGE("Invalid suffix.");
65             return false;
66         }
67         filter->SetSuffix(suffixs);
68     }
69     if (argv.HasProp("displayName")) {
70         vector<string> displayNames;
71         tie(ret, displayNames, ignore) = argv.GetProp("displayName").ToStringArray();
72         if (!ret) {
73             HILOGE("Failed to get displayname prop.");
74             return false;
75         }
76         if (displayNames.size() == 0) {
77             HILOGE("Invalid displayName.");
78             return false;
79         }
80         filter->SetDisplayName(displayNames);
81     }
82     if (argv.HasProp("fileSizeOver")) {
83         int64_t fileSizeOver;
84         tie(ret, fileSizeOver) = argv.GetProp("fileSizeOver").ToInt64();
85         if (!ret || fileSizeOver < 0) {
86             HILOGE("Failed to get fileSizeOver prop.");
87             return false;
88         }
89         filter->SetFileSizeOver(fileSizeOver);
90     }
91     if (argv.HasProp("lastModifiedAfter")) {
92         double lastModifiedAfter;
93         tie(ret, lastModifiedAfter) = argv.GetProp("lastModifiedAfter").ToDouble();
94         if (!ret || lastModifiedAfter < 0) {
95             HILOGE("Failed to get lastModifiedAfter prop.");
96             return false;
97         }
98         filter->SetLastModifiedAfter(lastModifiedAfter);
99     }
100     return true;
101 }
102 
GetOptionParam(const NVal & argv,OptionArgs * optionArgs)103 static bool GetOptionParam(const NVal &argv, OptionArgs *optionArgs)
104 {
105     bool succ = false;
106     if (argv.HasProp("listNum")) {
107         tie(succ, optionArgs->listNum) = argv.GetProp("listNum").ToInt64();
108         if (!succ) {
109             HILOGE("Failed to get listNum prop.");
110             return false;
111         } else if (optionArgs->listNum < 0) {
112             HILOGE("Invalid listNum.");
113             return false;
114         }
115     }
116 
117     if (argv.HasProp("recursion")) {
118         tie(succ, optionArgs->recursion) = argv.GetProp("recursion").ToBool();
119         if (!succ) {
120             HILOGE("Failed to get recursion prop.");
121             return false;
122         }
123     }
124 
125     if (argv.HasProp("filter")) {
126         NVal(filterProp) = argv.GetProp("filter");
127         auto ret = GetFileFilterParam(NVal(filterProp), &optionArgs->filter);
128         if (!ret) {
129             HILOGE("Failed to get filter prop.");
130             return false;
131         }
132     }
133     return true;
134 }
135 
GetOptionArg(napi_env env,const NFuncArg & funcArg,OptionArgs & optionArgs,const string & path)136 static bool GetOptionArg(napi_env env, const NFuncArg &funcArg, OptionArgs &optionArgs, const string &path)
137 {
138     auto options = NVal(env, funcArg[NARG_POS::SECOND]);
139     if (funcArg.GetArgc() >= NARG_CNT::TWO && options.TypeIs(napi_object)) {
140         if (!GetOptionParam(options, &optionArgs)) {
141             HILOGE("Invalid options");
142             return false;
143         }
144     } else if (funcArg.GetArgc() >= NARG_CNT::TWO && !options.TypeIs(napi_function)) {
145         HILOGE("Invalid options");
146         return false;
147     }
148     optionArgs.path = path;
149     return true;
150 }
151 
FilterSuffix(const vector<string> & suffixs,const struct dirent & filename)152 static bool FilterSuffix(const vector<string>& suffixs, const struct dirent& filename)
153 {
154     if (filename.d_type == DT_DIR) {
155         return true;
156     }
157     size_t found = string(filename.d_name).rfind('.');
158     if (found == std::string::npos) {
159         return false;
160     }
161     string suffixStr = string(filename.d_name).substr(found);
162     for (const auto &iter : suffixs) {
163         if (iter == suffixStr) {
164             return true;
165         }
166     }
167     return false;
168 }
169 
FilterDisplayname(const vector<string> & displaynames,const struct dirent & filename)170 static bool FilterDisplayname(const vector<string>& displaynames, const struct dirent& filename)
171 {
172     for (const auto &iter : displaynames) {
173         int ret = fnmatch(iter.c_str(), filename.d_name, FNM_PATHNAME | FNM_PERIOD);
174         if (ret == 0) {
175             return true;
176         }
177     }
178     return false;
179 }
180 
FilterFilesizeOver(const int64_t fFileSizeOver,const struct dirent & filename)181 static bool FilterFilesizeOver(const int64_t fFileSizeOver, const struct dirent& filename)
182 {
183     struct stat info;
184     string stPath = (g_optionArgs.path + '/' + string(filename.d_name));
185     int32_t res = stat(stPath.c_str(), &info);
186     if (res != 0) {
187         HILOGE("Failed to stat file.");
188         return false;
189     }
190     if (fFileSizeOver > info.st_size) {
191         return false;
192     }
193     return true;
194 }
195 
FilterLastModifyTime(const double lastModifiedAfter,const struct dirent & filename)196 static bool FilterLastModifyTime(const double lastModifiedAfter, const struct dirent& filename)
197 {
198     struct stat info;
199     string stPath = g_optionArgs.path + '/' + string(filename.d_name);
200     int32_t res = stat(stPath.c_str(), &info);
201     if (res != 0) {
202         HILOGE("Failed to stat file.");
203         return false;
204     }
205     if (lastModifiedAfter > time(&info.st_mtime)) {
206         return false;
207     }
208     return true;
209 }
210 
FilterResult(const struct dirent & filename)211 static bool FilterResult(const struct dirent &filename)
212 {
213     vector<string> fSuffixs = g_optionArgs.filter.GetSuffix();
214     if (!FilterSuffix(fSuffixs, filename) && fSuffixs.size() > 0) {
215         return false;
216     }
217     vector<string> fDisplaynames = g_optionArgs.filter.GetDisplayName();
218     if (!FilterDisplayname(fDisplaynames, filename) && fDisplaynames.size() > 0) {
219         return false;
220     }
221     int64_t fFileSizeOver = g_optionArgs.filter.GetFileSizeOver();
222     if (!FilterFilesizeOver(fFileSizeOver, filename) && fFileSizeOver > 0) {
223         return false;
224     }
225     double fLastModifiedAfter = g_optionArgs.filter.GetLastModifiedAfter();
226     if (!FilterLastModifyTime(fLastModifiedAfter, filename) && fLastModifiedAfter > 0) {
227         return false;
228     }
229     g_optionArgs.countNum++;
230     return true;
231 }
232 
FilterFunc(const struct dirent * filename)233 static int32_t FilterFunc(const struct dirent *filename)
234 {
235     if (string_view(filename->d_name) == "." || string_view(filename->d_name) == "..") {
236         return FILTER_DISMATCH;
237     }
238 
239     if (g_optionArgs.countNum < g_optionArgs.listNum || g_optionArgs.listNum == 0) {
240         if ((filename->d_type == DT_DIR && g_optionArgs.recursion) || FilterResult(*filename)) {
241             return FILTER_MATCH;
242         }
243     }
244     return FILTER_DISMATCH;
245 }
246 
FilterFileRes(const string & path)247 static vector<struct dirent> FilterFileRes(const string &path)
248 {
249     struct dirent** namelist;
250     int num = scandir(path.c_str(), &(namelist), FilterFunc, alphasort);
251     vector<struct dirent> dirents;
252     for (int i = 0; i < num; i++) {
253         struct dirent tmpDirent;
254         if (EOK == memcpy_s(&tmpDirent, sizeof(dirent), namelist[i], namelist[i]->d_reclen)) {
255             dirents.emplace_back(tmpDirent);
256         }
257         free(namelist[i]);
258     }
259     free(namelist);
260     return dirents;
261 }
262 
RecursiveFunc(const string & path,vector<struct dirent> & dirents)263 static void RecursiveFunc(const string &path, vector<struct dirent> &dirents)
264 {
265     struct dirent** namelist;
266     int num = scandir(path.c_str(), &(namelist), FilterFunc, alphasort);
267     for (int i = 0; i < num; i++) {
268         if ((*namelist[i]).d_type == DT_REG) {
269             struct dirent tmpDirent;
270             if (EOK == memcpy_s(&tmpDirent, sizeof(dirent), namelist[i], namelist[i]->d_reclen)) {
271                 dirents.emplace_back(tmpDirent);
272             }
273         } else if ((*(namelist)[i]).d_type == DT_DIR) {
274             string pathTemp = g_optionArgs.path;
275             g_optionArgs.path += '/' + string((*namelist[i]).d_name);
276             RecursiveFunc(g_optionArgs.path, dirents);
277             g_optionArgs.path = pathTemp;
278         }
279         free(namelist[i]);
280     }
281     free(namelist);
282 }
283 
DoListFileVector2NV(napi_env env,vector<dirent> & dirents)284 static napi_value DoListFileVector2NV(napi_env env, vector<dirent> &dirents)
285 {
286     vector<string> strs;
287     for (size_t i = 0; i < dirents.size(); i++) {
288         strs.emplace_back(dirents[i].d_name);
289     }
290     return NVal::CreateArrayString(env, strs).val_;
291 }
292 
DoListFileCompile(napi_env env,NError err,shared_ptr<ListFileArgs> arg)293 static NVal DoListFileCompile(napi_env env, NError err, shared_ptr<ListFileArgs> arg)
294 {
295     if (err) {
296         return { env, err.GetNapiErr(env) };
297     } else {
298         return { env, DoListFileVector2NV(env, arg->dirents) };
299     }
300 }
301 
Sync(napi_env env,napi_callback_info info)302 napi_value ListFile::Sync(napi_env env, napi_callback_info info)
303 {
304     NFuncArg funcArg(env, info);
305     if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::TWO)) {
306         HILOGE("Number of arguments unmatched");
307         NError(EINVAL).ThrowErr(env);
308         return nullptr;
309     }
310     auto [succPath, path, unused] = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8String();
311     if (!succPath) {
312         HILOGE("Invalid path");
313         NError(EINVAL).ThrowErr(env);
314         return nullptr;
315     }
316     if (!GetOptionArg(env, funcArg, g_optionArgs, string(path.get()))) {
317         NError(EINVAL).ThrowErr(env);
318         return nullptr;
319     }
320     vector<struct dirent> direntsRes;
321     if (g_optionArgs.recursion) {
322         RecursiveFunc(path.get(), direntsRes);
323     } else {
324         direntsRes = FilterFileRes(path.get());
325     }
326     g_optionArgs.Clear();
327     return DoListFileVector2NV(env, direntsRes);
328 }
329 
Async(napi_env env,napi_callback_info info)330 napi_value ListFile::Async(napi_env env, napi_callback_info info)
331 {
332     NFuncArg funcArg(env, info);
333     if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::THREE)) {
334         HILOGE("Number of arguments unmatched");
335         NError(EINVAL).ThrowErr(env);
336         return nullptr;
337     }
338 
339     auto [succPath, path, unused] = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8String();
340     if (!succPath) {
341         HILOGE("Invalid path");
342         NError(EINVAL).ThrowErr(env);
343         return nullptr;
344     }
345 
346     OptionArgs optionArgsTmp = {};
347     if (!GetOptionArg(env, funcArg, optionArgsTmp, string(path.get()))) {
348         NError(EINVAL).ThrowErr(env);
349         return nullptr;
350     }
351 
352     auto arg = make_shared<ListFileArgs>();
353     if (!arg) {
354         HILOGE("Failed to request heap memory.");
355         NError(ENOMEM).ThrowErr(env);
356         return nullptr;
357     }
358 
359     auto cbExec = [arg, optionArgsTmp]() -> NError {
360         g_optionArgs = optionArgsTmp;
361         if (g_optionArgs.recursion) {
362             RecursiveFunc(g_optionArgs.path, arg->dirents);
363         } else {
364             arg->dirents = FilterFileRes(g_optionArgs.path);
365         }
366         g_optionArgs.Clear();
367         return NError(ERRNO_NOERR);
368     };
369 
370     auto cbCompl = [arg](napi_env env, NError err) -> NVal {
371         if (err) {
372             return { env, err.GetNapiErr(env) };
373         }
374         return DoListFileCompile(env, err, arg);
375     };
376 
377     NVal thisVar(env, funcArg.GetThisVar());
378 
379     if (funcArg.GetArgc() == NARG_CNT::ONE || (funcArg.GetArgc() == NARG_CNT::TWO &&
380             NVal(env, funcArg[NARG_POS::SECOND]).TypeIs(napi_object))) {
381         return NAsyncWorkPromise(env, thisVar).Schedule(LIST_FILE_PRODUCE_NAME, cbExec, cbCompl).val_;
382     } else {
383         int32_t cbIdx = (funcArg.GetArgc() == NARG_CNT::TWO) ? NARG_POS::SECOND : NARG_POS::THIRD;
384         NVal cb(env, funcArg[cbIdx]);
385         return NAsyncWorkCallback(env, thisVar, cb).Schedule(LIST_FILE_PRODUCE_NAME, cbExec, cbCompl).val_;
386     }
387 }
388 } // namespace OHOS::FileManagement::ModuleFileIO