1 /*
2 * Copyright (c) 2021-2022 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 "dir_n_exporter.h"
17
18 #include <dirent.h>
19 #include <iostream>
20 #include <memory>
21 #include <mutex>
22 #include <sstream>
23
24 #include "dir_entity.h"
25 #include "securec.h"
26 #include "../../common/napi/n_async/n_async_work_callback.h"
27 #include "../../common/napi/n_async/n_async_work_promise.h"
28 #include "../../common/napi/n_class.h"
29 #include "../../common/napi/n_func_arg.h"
30 #include "../class_dirent/dirent_entity.h"
31 #include "../class_dirent/dirent_n_exporter.h"
32 #include "../common_func.h"
33
34 namespace OHOS {
35 namespace DistributedFS {
36 namespace ModuleFileIO {
37 using namespace std;
38
GetDirEntity(napi_env env,napi_callback_info info)39 static DirEntity *GetDirEntity(napi_env env, napi_callback_info info)
40 {
41 NFuncArg funcArg(env, info);
42 if (!funcArg.InitArgs(NARG_CNT::ZERO)) {
43 UniError(EINVAL).ThrowErr(env, "Number of arguments unmatched");
44 return nullptr;
45 }
46
47 auto dirEntity = NClass::GetEntityOf<DirEntity>(env, funcArg.GetThisVar());
48 if (!dirEntity) {
49 UniError(EIO).ThrowErr(env, "Cannot get entity of Dir");
50 return nullptr;
51 }
52 return dirEntity;
53 }
54
CloseSync(napi_env env,napi_callback_info info)55 napi_value DirNExporter::CloseSync(napi_env env, napi_callback_info info)
56 {
57 DirEntity *dirEntity = GetDirEntity(env, info);
58 if (!dirEntity || !dirEntity->dir_) {
59 UniError(EBADF).ThrowErr(env, "Dir has been closed yet");
60 return nullptr;
61 }
62
63 lock_guard(dirEntity->lock_);
64 dirEntity->dir_.reset();
65 return nullptr;
66 }
67
Close(napi_env env,napi_callback_info info)68 napi_value DirNExporter::Close(napi_env env, napi_callback_info info)
69 {
70 NFuncArg funcArg(env, info);
71 if (!funcArg.InitArgs(NARG_CNT::ZERO, NARG_CNT::ONE)) {
72 UniError(EINVAL).ThrowErr(env, "Number of arguments unmatched");
73 return nullptr;
74 }
75
76 auto dirEntity = NClass::GetEntityOf<DirEntity>(env, funcArg.GetThisVar());
77 if (!dirEntity) {
78 UniError(EIO).ThrowErr(env, "Cannot get entity of Dir");
79 return nullptr;
80 }
81
82 if (dirEntity->dir_ == nullptr) {
83 UniError(EBADF).ThrowErr(env, "Dir has been closed yet");
84 return nullptr;
85 }
86
87 auto cbExec = [dirEntity](napi_env env) -> UniError {
88 lock_guard(dirEntity->lock_);
89 DIR *dir = dirEntity->dir_.release();
90 int ret = closedir(dir);
91 if (ret == -1) {
92 return UniError(errno);
93 } else {
94 return UniError(ERRNO_NOERR);
95 }
96 };
97 auto cbCompl = [](napi_env env, UniError err) -> NVal {
98 if (err) {
99 return { env, err.GetNapiErr(env) };
100 } else {
101 return NVal::CreateUndefined(env);
102 }
103 };
104
105 NVal thisVar(env, funcArg.GetThisVar());
106 static const string PROCEDURE_NAME = "fileioDirClose";
107 if (funcArg.GetArgc() == NARG_CNT::ZERO) {
108 return NAsyncWorkPromise(env, thisVar).Schedule(PROCEDURE_NAME, cbExec, cbCompl).val_;
109 } else {
110 NVal cb(env, funcArg[NARG_POS::FIRST]);
111 return NAsyncWorkCallback(env, thisVar, cb).Schedule(PROCEDURE_NAME, cbExec, cbCompl).val_;
112 }
113 }
114
115 struct DirReadArgs {
116 NRef thisptrRef_;
117 struct dirent dirRes = {
118 .d_ino = 0,
119 .d_off = 0,
120 .d_reclen = 0,
121 .d_type = 0,
122 .d_name = { '\0' },
123 };
DirReadArgsOHOS::DistributedFS::ModuleFileIO::DirReadArgs124 explicit DirReadArgs(NVal obj) : thisptrRef_(obj) {}
125 };
126
DoReadComplete(napi_env env,UniError err,shared_ptr<DirReadArgs> arg)127 static NVal DoReadComplete(napi_env env, UniError err, shared_ptr<DirReadArgs> arg)
128 {
129 if (err) {
130 return { env, err.GetNapiErr(env) };
131 } else {
132 napi_value objDirent = NClass::InstantiateClass(env, DirentNExporter::className_, {});
133 if (!objDirent) {
134 return { env, UniError(EINVAL).GetNapiErr(env) };
135 }
136 auto direntEntity = NClass::GetEntityOf<DirentEntity>(env, objDirent);
137 if (!direntEntity) {
138 return { env, UniError(EINVAL).GetNapiErr(env) };
139 }
140
141 if (strlen(arg->dirRes.d_name) == 0) {
142 return { env, NVal::CreateUndefined(env).val_ };
143 } else {
144 direntEntity->dirent_ = arg->dirRes;
145 return { env, objDirent };
146 }
147 }
148 }
149
Read(napi_env env,napi_callback_info info)150 napi_value DirNExporter::Read(napi_env env, napi_callback_info info)
151 {
152 NFuncArg funcArg(env, info);
153 if (!funcArg.InitArgs(NARG_CNT::ZERO, NARG_CNT::ONE)) {
154 UniError(EINVAL).ThrowErr(env, "Number of arguments unmatched");
155 return nullptr;
156 }
157
158 auto dirEntity = NClass::GetEntityOf<DirEntity>(env, funcArg.GetThisVar());
159 if (!dirEntity) {
160 UniError(EIO).ThrowErr(env, "Cannot get entity of Dir");
161 return nullptr;
162 }
163
164 if (!dirEntity || !dirEntity->dir_) {
165 UniError(EBADF).ThrowErr(env, "Dir has been closed yet");
166 return nullptr;
167 }
168
169 DIR *dir = dirEntity->dir_.get();
170 auto arg = make_shared<DirReadArgs>(NVal(env, funcArg.GetThisVar()));
171 auto cbExec = [arg, dir, dirEntity](napi_env env) -> UniError {
172 struct dirent tmpDirent;
173 lock_guard(dirEntity->lock_);
174 errno = 0;
175 dirent *res = nullptr;
176 do {
177 res = readdir(dir);
178 if (res == nullptr && errno) {
179 return UniError(errno);
180 } else if (res == nullptr) {
181 return UniError(ERRNO_NOERR);
182 } else if (string(res->d_name) == "." || string(res->d_name) == "..") {
183 continue;
184 } else {
185 if (EOK != memcpy_s(&tmpDirent, sizeof(dirent), res, res->d_reclen)) {
186 return UniError(errno);
187 }
188 break;
189 }
190 } while (true);
191
192 arg->dirRes = tmpDirent;
193 return UniError(ERRNO_NOERR);
194 };
195 auto cbCompl = [arg](napi_env env, UniError err) -> NVal {
196 return DoReadComplete(env, err, arg);
197 };
198 NVal thisVar(env, funcArg.GetThisVar());
199 const string procedureName = "fileioDirRead";
200 if (funcArg.GetArgc() == NARG_CNT::ZERO) {
201 return NAsyncWorkPromise(env, thisVar).Schedule(procedureName, cbExec, cbCompl).val_;
202 } else {
203 NVal cb(env, funcArg[NARG_POS::FIRST]);
204 return NAsyncWorkCallback(env, thisVar, cb).Schedule(procedureName, cbExec, cbCompl).val_;
205 }
206 }
207
ReadSync(napi_env env,napi_callback_info info)208 napi_value DirNExporter::ReadSync(napi_env env, napi_callback_info info)
209 {
210 NFuncArg funcArg(env, info);
211 if (!funcArg.InitArgs(NARG_CNT::ZERO)) {
212 UniError(EINVAL).ThrowErr(env, "Number of arguments unmatched");
213 return nullptr;
214 }
215
216 DirEntity *dirEntity = GetDirEntity(env, info);
217 if (!dirEntity || !dirEntity->dir_) {
218 UniError(EBADF).ThrowErr(env, "Dir has been closed yet");
219 return nullptr;
220 }
221 DIR *dir = dirEntity->dir_.get();
222
223 struct dirent tmpDirent;
224 {
225 lock_guard(dirEntity->lock_);
226 errno = 0;
227 dirent *res = nullptr;
228 do {
229 res = readdir(dir);
230 if (res == nullptr && errno) {
231 UniError(errno).ThrowErr(env);
232 return nullptr;
233 } else if (res == nullptr) {
234 return NVal::CreateUndefined(env).val_;
235 } else if (string(res->d_name) == "." || string(res->d_name) == "..") {
236 continue;
237 } else {
238 if (EOK != memcpy_s(&tmpDirent, sizeof(dirent), res, res->d_reclen)) {
239 UniError(errno).ThrowErr(env);
240 return nullptr;
241 }
242 break;
243 }
244 } while (true);
245 }
246
247 napi_value objDirent = NClass::InstantiateClass(env, DirentNExporter::className_, {});
248 if (!objDirent) {
249 return nullptr;
250 }
251
252 auto direntEntity = NClass::GetEntityOf<DirentEntity>(env, objDirent);
253 if (!direntEntity) {
254 return nullptr;
255 }
256 direntEntity->dirent_ = tmpDirent;
257
258 return objDirent;
259 }
260
261 struct DirListFileArgs {
262 vector<dirent> dirents;
DirListFileArgsOHOS::DistributedFS::ModuleFileIO::DirListFileArgs263 explicit DirListFileArgs()
264 {
265 dirents = vector<dirent>();
266 }
267 ~DirListFileArgs() = default;
268 };
269
CheckDirEntity(napi_env env,napi_value dir_entity)270 static DirEntity *CheckDirEntity(napi_env env, napi_value dir_entity)
271 {
272 auto dirEntity = NClass::GetEntityOf<DirEntity>(env, dir_entity);
273 if (!dirEntity) {
274 UniError(EIO).ThrowErr(env, "Cannot get entity of Dir");
275 return nullptr;
276 }
277
278 if (!dirEntity || !dirEntity->dir_) {
279 UniError(EBADF).ThrowErr(env, "Dir has been closed yet");
280 return nullptr;
281 }
282 return dirEntity;
283 }
284
ParseJsListNum(napi_env env,napi_value listNumFromJs)285 static tuple<bool, int> ParseJsListNum(napi_env env, napi_value listNumFromJs)
286 {
287 auto [succ, listNum] = NVal(env, listNumFromJs).ToInt32();
288 return {succ, listNum};
289 }
290
DoListFileVector2NV(napi_env env,vector<dirent> dirents)291 static napi_value DoListFileVector2NV(napi_env env, vector<dirent> dirents)
292 {
293 napi_value res = nullptr;
294 napi_create_array(env, &res);
295 for (size_t i = 0; i < dirents.size(); i++) {
296 napi_value objDirent = NClass::InstantiateClass(env, DirentNExporter::className_, {});
297 if (!objDirent) {
298 UniError(EINVAL).ThrowErr(env);
299 return nullptr;
300 }
301 auto direntEntity = NClass::GetEntityOf<DirentEntity>(env, objDirent);
302 if (!direntEntity) {
303 UniError(EINVAL).ThrowErr(env);
304 return nullptr;
305 }
306 direntEntity->dirent_ = dirents[i];
307 napi_set_element(env, res, i, objDirent);
308 }
309 return res;
310 }
311
DoListFileCompile(napi_env env,UniError err,shared_ptr<DirListFileArgs> arg)312 static NVal DoListFileCompile(napi_env env, UniError err, shared_ptr<DirListFileArgs> arg)
313 {
314 if (err) {
315 return { env, err.GetNapiErr(env) };
316 } else {
317 return { env, DoListFileVector2NV(env, arg->dirents) };
318 }
319 }
320
ListFile(napi_env env,napi_callback_info info)321 napi_value DirNExporter::ListFile(napi_env env, napi_callback_info info)
322 {
323 NFuncArg funcArg(env, info);
324 if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::TWO)) {
325 UniError(EINVAL).ThrowErr(env, "Number of arguments unmatched");
326 return nullptr;
327 }
328 auto dirEntity = CheckDirEntity(env, funcArg.GetThisVar());
329 if (!dirEntity) {
330 return nullptr;
331 }
332 auto [succ, num] = ParseJsListNum(env, funcArg[NARG_POS::FIRST]);
333 if (!succ) {
334 UniError(EINVAL).ThrowErr(env, "Invalid listNum");
335 return nullptr;
336 }
337
338 DIR *dir = dirEntity->dir_.get();
339 auto arg = make_shared<DirListFileArgs>();
340 int listNum = num;
341 auto cbExec = [arg, dir, dirEntity, listNum](napi_env env) -> UniError {
342 lock_guard(dirEntity->lock_);
343 dirent *res = nullptr;
344 int listCount = 0;
345 do {
346 errno = 0;
347 res = readdir(dir);
348 if (res == nullptr && errno) {
349 return UniError(errno);
350 } else if (res == nullptr) {
351 return UniError(ERRNO_NOERR);
352 } else if (string(res->d_name) == "." || string(res->d_name) == "..") {
353 continue;
354 } else {
355 arg->dirents.push_back(*res);
356 listCount++;
357 }
358 } while (listCount < listNum || listNum == 0);
359 return UniError(ERRNO_NOERR);
360 };
361 auto cbCompl = [arg](napi_env env, UniError err) -> NVal {
362 return DoListFileCompile(env, err, arg);
363 };
364 NVal thisVar(env, funcArg.GetThisVar());
365
366 if (funcArg.GetArgc() == NARG_CNT::ONE) {
367 return NAsyncWorkPromise(env, thisVar).Schedule(listfileProcedureName, cbExec, cbCompl).val_;
368 } else {
369 NVal cb(env, funcArg[NARG_POS::SECOND]);
370 return NAsyncWorkCallback(env, thisVar, cb).Schedule(listfileProcedureName, cbExec, cbCompl).val_;
371 }
372 }
373
ListFileSync(napi_env env,napi_callback_info info)374 napi_value DirNExporter::ListFileSync(napi_env env, napi_callback_info info)
375 {
376 NFuncArg funcArg(env, info);
377 if (!funcArg.InitArgs(NARG_CNT::ONE)) {
378 UniError(EINVAL).ThrowErr(env, "Number of arguments unmatched");
379 return nullptr;
380 }
381 auto dirEntity = CheckDirEntity(env, funcArg.GetThisVar());
382 if (!dirEntity) {
383 return nullptr;
384 }
385 auto [succ, listNum] = ParseJsListNum(env, funcArg[NARG_POS::FIRST]);
386 if (!succ) {
387 UniError(EINVAL).ThrowErr(env, "Invalid listNum");
388 return nullptr;
389 }
390
391 vector<dirent> dirents;
392 {
393 lock_guard(dirEntity->lock_);
394 dirent *res = nullptr;
395 int listCount = 0;
396 auto dir = dirEntity->dir_.get();
397 do {
398 errno = 0;
399 res = readdir(dir);
400 if (res == nullptr && errno) {
401 UniError(errno).ThrowErr(env);
402 return nullptr;
403 } else if (res == nullptr) {
404 break;
405 } else if (string(res->d_name) == "." || string(res->d_name) == "..") {
406 continue;
407 } else {
408 dirents.push_back(*res);
409 listCount++;
410 }
411 } while (listCount < listNum || listNum == 0);
412 }
413 return DoListFileVector2NV(env, dirents);
414 }
415
Constructor(napi_env env,napi_callback_info info)416 napi_value DirNExporter::Constructor(napi_env env, napi_callback_info info)
417 {
418 NFuncArg funcArg(env, info);
419 if (!funcArg.InitArgs(NARG_CNT::ZERO)) {
420 UniError(EINVAL).ThrowErr(env, "Number of arguments unmatched");
421 return nullptr;
422 }
423
424 auto dirEntity = make_unique<DirEntity>();
425 if (!NClass::SetEntityFor<DirEntity>(env, funcArg.GetThisVar(), move(dirEntity))) {
426 UniError(EIO).ThrowErr(env, "INNER BUG. Failed to wrap entity for obj dir");
427 return nullptr;
428 }
429 return funcArg.GetThisVar();
430 }
431
Export()432 bool DirNExporter::Export()
433 {
434 vector<napi_property_descriptor> props = {
435 NVal::DeclareNapiFunction("readSync", ReadSync),
436 NVal::DeclareNapiFunction("closeSync", CloseSync),
437 NVal::DeclareNapiFunction("listfileSync", ListFileSync),
438 NVal::DeclareNapiFunction("read", Read),
439 NVal::DeclareNapiFunction("close", Close),
440 NVal::DeclareNapiFunction("listfile", ListFile),
441 };
442
443 string className = GetClassName();
444 bool succ = false;
445 napi_value classValue = nullptr;
446 tie(succ, classValue) = NClass::DefineClass(exports_.env_, className, DirNExporter::Constructor, std::move(props));
447 if (!succ) {
448 UniError(EIO).ThrowErr(exports_.env_, "INNER BUG. Failed to define class Dirent");
449 return false;
450 }
451
452 succ = NClass::SaveClass(exports_.env_, className, classValue);
453 if (!succ) {
454 UniError(EIO).ThrowErr(exports_.env_, "INNER BUG. Failed to save class Dirent");
455 return false;
456 }
457
458 return exports_.AddProp(className, classValue);
459 }
460
GetClassName()461 string DirNExporter::GetClassName()
462 {
463 return DirNExporter::className_;
464 }
465
DirNExporter(napi_env env,napi_value exports)466 DirNExporter::DirNExporter(napi_env env, napi_value exports) : NExporter(env, exports) {}
467
~DirNExporter()468 DirNExporter::~DirNExporter() {}
469 } // namespace ModuleFileIO
470 } // namespace DistributedFS
471 } // namespace OHOS
472