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