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