• 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 <string>
21 #include <string_view>
22 #include <sys/stat.h>
23 #include <thread>
24 #include <tuple>
25 
26 #include "file_utils.h"
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,FileFilter * filter)53 static bool GetFileFilterParam(const NVal &argv, FileFilter *filter)
54 {
55     bool ret = false;
56     if (argv.HasProp("suffix") && !argv.GetProp("suffix").TypeIs(napi_undefined)) {
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") && !argv.GetProp("displayName").TypeIs(napi_undefined)) {
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") && !argv.GetProp("fileSizeOver").TypeIs(napi_undefined)) {
83         int64_t fileSizeOver = 0;
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") && !argv.GetProp("lastModifiedAfter").TypeIs(napi_undefined)) {
92         double lastModifiedAfter = 0;
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(0);
108         if (!succ || optionArgs->listNum < 0) {
109             HILOGE("Failed to get listNum prop");
110             return false;
111         }
112     }
113 
114     if (argv.HasProp("recursion")) {
115         tie(succ, optionArgs->recursion) = argv.GetProp("recursion").ToBool(false);
116         if (!succ) {
117             HILOGE("Failed to get recursion prop.");
118             return false;
119         }
120     }
121 
122     if (argv.HasProp("filter")) {
123         NVal filterProp = argv.GetProp("filter");
124         if (!filterProp.TypeIs(napi_undefined)) {
125             auto ret = GetFileFilterParam(filterProp, &optionArgs->filter);
126             if (!ret) {
127                 HILOGE("Failed to get filter prop.");
128                 return false;
129             }
130         }
131     }
132     return true;
133 }
134 
GetOptionArg(napi_env env,const NFuncArg & funcArg,OptionArgs & optionArgs,const string & path)135 static bool GetOptionArg(napi_env env, const NFuncArg &funcArg, OptionArgs &optionArgs, const string &path)
136 {
137     optionArgs.path = path;
138     if (funcArg.GetArgc() == NARG_CNT::ONE) {
139         return true;
140     }
141     if (funcArg.GetArgc() >= NARG_CNT::TWO) {
142         auto options = NVal(env, funcArg[NARG_POS::SECOND]);
143         if (options.TypeIs(napi_object)) {
144             return GetOptionParam(options, &optionArgs);
145         } else if (options.TypeIs(napi_undefined) || options.TypeIs(napi_function)) {
146             return true;
147         }
148     }
149     return false;
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     if (fFileSizeOver < 0) {
184         return true;
185     }
186     struct stat info;
187     string stPath = (g_optionArgs.path + '/' + string(filename.d_name));
188     int32_t res = stat(stPath.c_str(), &info);
189     if (res != 0) {
190         HILOGE("Failed to stat file.");
191         return false;
192     }
193     if (info.st_size > fFileSizeOver) {
194         return true;
195     }
196     return false;
197 }
198 
FilterLastModifyTime(const double lastModifiedAfter,const struct dirent & filename)199 static bool FilterLastModifyTime(const double lastModifiedAfter, const struct dirent &filename)
200 {
201     if (lastModifiedAfter < 0) {
202         return true;
203     }
204     struct stat info;
205     string stPath = g_optionArgs.path + '/' + string(filename.d_name);
206     int32_t res = stat(stPath.c_str(), &info);
207     if (res != 0) {
208         HILOGE("Failed to stat file.");
209         return false;
210     }
211     if (static_cast<double>(info.st_mtime) > lastModifiedAfter) {
212         return true;
213     }
214     return false;
215 }
216 
FilterResult(const struct dirent & filename)217 static bool FilterResult(const struct dirent &filename)
218 {
219     vector<string> fSuffixs = g_optionArgs.filter.GetSuffix();
220     if (!FilterSuffix(fSuffixs, filename) && fSuffixs.size() > 0) {
221         return false;
222     }
223     vector<string> fDisplaynames = g_optionArgs.filter.GetDisplayName();
224     if (!FilterDisplayname(fDisplaynames, filename) && fDisplaynames.size() > 0) {
225         return false;
226     }
227     int64_t fFileSizeOver = g_optionArgs.filter.GetFileSizeOver();
228     if (!FilterFilesizeOver(fFileSizeOver, filename)) {
229         return false;
230     }
231     double fLastModifiedAfter = g_optionArgs.filter.GetLastModifiedAfter();
232     if (!FilterLastModifyTime(fLastModifiedAfter, filename)) {
233         return false;
234     }
235     g_optionArgs.countNum++;
236     return true;
237 }
238 
FilterFunc(const struct dirent * filename)239 static int32_t FilterFunc(const struct dirent *filename)
240 {
241     if (string_view(filename->d_name) == "." || string_view(filename->d_name) == "..") {
242         return FILTER_DISMATCH;
243     }
244 
245     if (g_optionArgs.countNum < g_optionArgs.listNum || g_optionArgs.listNum == 0) {
246         if ((filename->d_type == DT_DIR && g_optionArgs.recursion) || FilterResult(*filename)) {
247             return FILTER_MATCH;
248         }
249     }
250     return FILTER_DISMATCH;
251 }
252 
Deleter(struct NameListArg * arg)253 static void Deleter(struct NameListArg *arg)
254 {
255     for (int i = 0; i < arg->direntNum; i++) {
256         free((arg->namelist)[i]);
257         (arg->namelist)[i] = nullptr;
258     }
259     free(arg->namelist);
260 }
261 
FilterFileRes(const string & path,vector<string> & dirents)262 static int FilterFileRes(const string &path, vector<string> &dirents)
263 {
264     unique_ptr<struct NameListArg, decltype(Deleter)*> pNameList = { new (nothrow) struct NameListArg, Deleter };
265     if (!pNameList) {
266         HILOGE("Failed to request heap memory.");
267         return ENOMEM;
268     }
269     int num = scandir(path.c_str(), &(pNameList->namelist), FilterFunc, nullptr);
270     if (num < 0) {
271         HILOGE("Failed to scan dir");
272         return errno;
273     }
274     pNameList->direntNum = num;
275     for (int i = 0; i < num; i++) {
276         dirents.emplace_back(pNameList->namelist[i]->d_name);
277     }
278     return ERRNO_NOERR;
279 }
280 
RecursiveFunc(const string & path,vector<string> & dirents)281 static int RecursiveFunc(const string &path, vector<string> &dirents)
282 {
283     unique_ptr<struct NameListArg, decltype(Deleter)*> pNameList = { new (nothrow) struct NameListArg, Deleter };
284     if (!pNameList) {
285         HILOGE("Failed to request heap memory.");
286         return ENOMEM;
287     }
288     int num = scandir(path.c_str(), &(pNameList->namelist), FilterFunc, nullptr);
289     if (num < 0) {
290         HILOGE("Failed to scan dir");
291         return errno;
292     }
293     pNameList->direntNum = num;
294     for (int i = 0; i < num; i++) {
295         if ((*(pNameList->namelist[i])).d_type == DT_REG) {
296             dirents.emplace_back(path + '/' + pNameList->namelist[i]->d_name);
297         } else if ((*(pNameList->namelist[i])).d_type == DT_DIR) {
298             string pathTemp = g_optionArgs.path;
299             g_optionArgs.path += '/' + string((*(pNameList->namelist[i])).d_name);
300             int ret = RecursiveFunc(g_optionArgs.path, dirents);
301             if (ret != ERRNO_NOERR) {
302                 return ret;
303             }
304             g_optionArgs.path = pathTemp;
305         }
306     }
307     return ERRNO_NOERR;
308 }
309 
DoListFileVector2NV(napi_env env,const string & path,vector<string> & dirents,bool recursion)310 static napi_value DoListFileVector2NV(napi_env env, const string &path, vector<string> &dirents, bool recursion)
311 {
312     if (recursion) {
313         for (size_t i = 0; i < dirents.size(); i++) {
314             dirents[i] = dirents[i].substr(path.length());
315         }
316     }
317     return NVal::CreateArrayString(env, dirents).val_;
318 }
319 
Sync(napi_env env,napi_callback_info info)320 napi_value ListFile::Sync(napi_env env, napi_callback_info info)
321 {
322     NFuncArg funcArg(env, info);
323     if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::TWO)) {
324         HILOGE("Number of arguments unmatched");
325         NError(EINVAL).ThrowErr(env);
326         return nullptr;
327     }
328     auto [succPath, path, unused] = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8String();
329     if (!succPath) {
330         HILOGE("Invalid path");
331         NError(EINVAL).ThrowErr(env);
332         return nullptr;
333     }
334     if (!GetOptionArg(env, funcArg, g_optionArgs, string(path.get()))) {
335         HILOGE("Invalid options");
336         NError(EINVAL).ThrowErr(env);
337         return nullptr;
338     }
339     vector<string> direntsRes;
340     int ret = 0;
341     ret = g_optionArgs.recursion ? RecursiveFunc(path.get(), direntsRes) : FilterFileRes(path.get(), direntsRes);
342     if (ret) {
343         NError(ret).ThrowErr(env);
344         return nullptr;
345     }
346     auto res = DoListFileVector2NV(env, string(path.get()), direntsRes, g_optionArgs.recursion);
347     g_optionArgs.Clear();
348     return res;
349 }
350 
Async(napi_env env,napi_callback_info info)351 napi_value ListFile::Async(napi_env env, napi_callback_info info)
352 {
353     NFuncArg funcArg(env, info);
354     if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::THREE)) {
355         HILOGE("Number of arguments unmatched");
356         NError(EINVAL).ThrowErr(env);
357         return nullptr;
358     }
359 
360     auto [succPath, path, unused] = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8String();
361     if (!succPath) {
362         HILOGE("Invalid path");
363         NError(EINVAL).ThrowErr(env);
364         return nullptr;
365     }
366 
367     OptionArgs optionArgsTmp = {};
368     if (!GetOptionArg(env, funcArg, optionArgsTmp, string(path.get()))) {
369         HILOGE("Invalid options");
370         NError(EINVAL).ThrowErr(env);
371         return nullptr;
372     }
373 
374     auto arg = CreateSharedPtr<ListFileArgs>();
375     if (arg == nullptr) {
376         HILOGE("Failed to request heap memory.");
377         NError(ENOMEM).ThrowErr(env);
378         return nullptr;
379     }
380     auto cbExec = [arg, optionArgsTmp]() -> NError {
381         g_optionArgs = optionArgsTmp;
382         int ret = 0;
383         ret = g_optionArgs.recursion ? RecursiveFunc(g_optionArgs.path, arg->dirents) :
384             FilterFileRes(g_optionArgs.path, arg->dirents);
385         g_optionArgs.Clear();
386         return ret ? NError(ret) : NError(ERRNO_NOERR);
387     };
388 
389     auto cbCompl = [arg, optionArgsTmp, path = string(path.get())](napi_env env, NError err) -> NVal {
390         if (err) {
391             return { env, err.GetNapiErr(env) };
392         }
393         return { env, DoListFileVector2NV(env, path, arg->dirents, optionArgsTmp.recursion) };
394     };
395 
396     NVal thisVar(env, funcArg.GetThisVar());
397 
398     if (funcArg.GetArgc() == NARG_CNT::ONE || (funcArg.GetArgc() == NARG_CNT::TWO &&
399            !NVal(env, funcArg[NARG_POS::SECOND]).TypeIs(napi_function))) {
400         return NAsyncWorkPromise(env, thisVar).Schedule(LIST_FILE_PRODUCE_NAME, cbExec, cbCompl).val_;
401     } else {
402         NVal cb(env, funcArg[((funcArg.GetArgc() == NARG_CNT::TWO) ? NARG_POS::SECOND : NARG_POS::THIRD)]);
403         return NAsyncWorkCallback(env, thisVar, cb).Schedule(LIST_FILE_PRODUCE_NAME, cbExec, cbCompl).val_;
404     }
405 }
406 } // namespace OHOS::FileManagement::ModuleFileIO